Saturday, September 8, 2012

Design Problem Sales Tax Problem Statement


Sales Tax Problem Statement :

 Basic sales tax is applicable at a rate of 10% on all goods, except books, food, and medical products that are exempt. Import duty is an additional sales tax applicable on all imported goods at a rate of 5%, with no exemptions.

When I purchase items I receive a receipt which lists the name of all the items and their price (including tax), finishing with the total cost of the items, and the total amounts of sales taxes paid. The rounding rules for sales tax are that for a tax rate of n%, a shelf price of p contains (np/100 rounded up to the nearest 0.05) amount of sales tax.

Write an application that prints out the receipt details for these shopping baskets...

INPUT:

Input 1:
1 book at 12.49
1 music CD at 14.99
1 chocolate bar at 0.85

Input 2:
1 imported box of chocolates at 10.00
1 imported bottle of perfume at 47.50

Input 3:
1 imported bottle of perfume at 27.99
1 bottle of perfume at 18.99

1 packet of headache pills at 9.75
1 box of imported chocolates at 11.25

OUTPUT

Output 1:
1 book : 12.49
1 music CD: 16.49
1 chocolate bar: 0.85
Sales Taxes: 1.50
Total: 29.83

Output 2:
1 imported box of chocolates: 10.50
1 imported bottle of perfume: 54.65
Sales Taxes: 7.65
Total: 65.15

Output 3:
1 imported bottle of perfume: 32.19
1 bottle of perfume: 20.89
1 packet of headache pills: 9.75
1 imported box of chocolates: 11.85
Sales Taxes: 6.70
Total: 74.68


Design consideration :

1)Design the solution using “Design by  Interface” approach . basically decoupled the implementation.

2) Class should be open for extension but close for modification

3) Sales tax get % get change then no code change required.

## exempt item tax  %
EXEMPT=0
## Import duty item tax %
IMPORTDUTY=5
##Basic sales tax %
BASICSALESTAX=10
##Basic sales tax +Import duty %
SALESTAXANDIMPORTDUTY=15

4) I have used decorator pattern and provided concrete implementation ,in future if required any changes in code and logic so wont change existing code will add another class and few method only

5) Used static factory pattern for object creation also implemented singleton pattern, only one thread is using it so no need any synchronization.

6) provided single entry( Service ) point for application and its also handle exceptions

7) Used JDK 1.7 for development

8) Using multi tier architecture

Assumption :
1) Display only out put as given in problem
2) Future may be calculation logic gets change
3) Future may be display logic gets change

Calculation based on following assumption

1) Basic sales tax is applicable at a rate of 10% on all goods,
2) Except books, food, and medical products that are exempt.
 3) Import duty is an additional sales  tax applicable on all imported goods at a rate of 5%, with no exemptions.
             
 When I purchase items I receive a receipt which lists the name of all the * items and their price (including tax),  finishing with the total cost of the items, and the total amounts of sales taxes paid.

The rounding rules for sales tax are that for a tax  rate of n%, a shelf price of p contains

 (np/100 rounded up to the nearest * 0.05) amount of sales tax.

           


                         :SHOPPING BILL:



=========================================================================
Quantity                                     Name                            Price
=========================================================================
1                      Book                             :12.50
1                      Music CD                      :16.50
1                      Chocolate bar               :0.85
--------------------------------------------------------------------------------------------------------------------------------
Sales Tax : 1.50
Total : 29.85
=========================================================================
                                   

                                               :SHOPPING BILL:

=========================================================================
Quantity                                     Name                            Price
=========================================================================
1                      Imported box of chocolates                   :10.50
1                      Imported bottle of perfume                           :54.65
--------------------------------------------------------------------------------------------------------------------------------
Sales Tax : 7.65
Total : 65.15
=========================================================================
                                   
                                                 :SHOPPING BILL:

=========================================================================
Quantity                                     Name                            Price
=========================================================================
1                      Imported bottle of perfume                    :32.20
1                      Bottle of perfume                                   :20.90
1                      Packet of headache pills                       :9.75
1                      Imported box of chocolates                   :11.85
--------------------------------------------------------------------------------------------------------------------------------
Sales Tax : 6.70
Total : 74.65
=========================================================================



1) ShoppingCartTestCase.java

Simple Junit test cases its contain three Testcase with following input data
Dependency : required Junit 3 or 4 jar

package com.thoughtworks.india.shoppingcart.salestax.junit.Shoppingcart;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

import junit.framework.TestCase;

import com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.dto.impl.InputDataDTO;
import com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.service.SalesTaxCalculatorServiceIfc;
import com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.service.impl.SalesTaxCalculatorServiceImpl;
/**
 * This is Junit test case for testing
 * @author Vaquar Khan
 *
 */
public class ShoppingCartTestCase extends TestCase {
                                                        /**
                                                         * Method purpose : INPUT:
                                                         *
                                                         * Input 1:
                                                         *   1 book at 12.49
                                                         *   1 music CD at 14.99
                                                         *   1 chocolate bar at 0.85
                                                         * Insert calculate tax
                                                         *
                                                         * @param
                                                         * @return void
                                                         */

                                                        public void testScenarioInput_001() {

                                                           
                                                            InputDataDTO inputDataDTO = null;
                                                            List<InputDataDTO> shoppingCartList = new ArrayList<InputDataDTO>();
                                                            //System.out.println("Input 1:");

                                                            // TODO Input 1: 1 book at 12.49
                                                            inputDataDTO = new InputDataDTO();
                                                            inputDataDTO.setGoodsName("Book");
                                                            inputDataDTO.setGoodsquontity(1);
                                                            inputDataDTO.setExemptTax(true);
                                                            inputDataDTO.setGoodsPrice(new BigDecimal(12.49));
                                                            //TODO System.out.println(inputDataDTO.toString());
                                                            //Add to list
                                                            shoppingCartList.add(inputDataDTO);
                                                           
                                                           
                                                            // 1 music CD at 14.99
                                                            inputDataDTO = new InputDataDTO();
                                                            inputDataDTO.setGoodsName("Music CD");
                                                            inputDataDTO.setGoodsquontity(1);
                                                            inputDataDTO.setGoodsPrice(new BigDecimal(14.99));
                                                            inputDataDTO.setDomesticTaxPercentage(true);
                                                            //TODO System.out.println(inputDataDTO.toString());
                                                            //Add to list
                                                            shoppingCartList.add(inputDataDTO);
                                                           

                                                            // 1 chocolate bar at 0.85
                                                            inputDataDTO = new InputDataDTO();
                                                            inputDataDTO.setGoodsName("Chocolate bar");
                                                            inputDataDTO.setGoodsquontity(1);
                                                            inputDataDTO.setGoodsPrice(new BigDecimal(0.85));
                                                            inputDataDTO.setExemptTax(true);
                                                            //TODO System.out.println(inputDataDTO.toString());
                                                            //Add to list
                                                            shoppingCartList.add(inputDataDTO);

                                                            try {
                                                                        // Creating the ShoppingCart
                                                                        SalesTaxCalculatorServiceIfc shopping = new SalesTaxCalculatorServiceImpl();
                                                                       
                                                                        // calculate data
                                                                        shopping.calculateTax(shoppingCartList);

                                                            } catch (Throwable e) {
                                                                        // This operation is not supported hence getting an error
                                                                        System.out.println(e.getLocalizedMessage());
                                                            }

                                                        }

                                                        /**
                                                         * Method purpose :
                                                         *  Input 2:
                                                         *  1 imported box of chocolates at 10.00
                                                         *  1 imported bottle of perfume at 47.50
                                                         *  Insert calculate tax
                                                         *
                                                         * @param
                                                         * @return void
                                                         */

                                                        public void testScenarioInput_002() {

                                                            InputDataDTO inputDataDTO = null;
                                                            List<InputDataDTO> shoppingCartList = new ArrayList<InputDataDTO>();
                                                            //System.out.println("Input 2:");
                                                            // TODO Input 2:
                                                            // 1 imported box of chocolates at 10.00

                                                            inputDataDTO = new InputDataDTO();
                                                            inputDataDTO.setGoodsName("Imported box of chocolates ");
                                                            inputDataDTO.setGoodsquontity(1);
                                                            inputDataDTO.setGoodsPrice(new BigDecimal(10.00));
                                                            inputDataDTO.setAdditionalImportedTaxPercentage(true);
                                                            //TODO System.out.println(inputDataDTO.toString());
                                                            //Add to list
                                                            shoppingCartList.add(inputDataDTO);

                                                            // 1 imported bottle of perfume at 47.50
                                                            inputDataDTO = new InputDataDTO();
                                                            inputDataDTO.setGoodsName("Imported bottle of perfume");
                                                            inputDataDTO.setGoodsquontity(1);
                                                            inputDataDTO.setGoodsPrice(new BigDecimal(47.50));
                                                            inputDataDTO.setAdditionalImportedTaxPercentage(true);
                                                            inputDataDTO.setDomesticTaxPercentage(true);
                                                            //TODO System.out.println(inputDataDTO.toString());
                                                            //Add to List
                                                            shoppingCartList.add(inputDataDTO);
                                                            try {
                                                                        // Creating the ShoppingCart
                                                                        SalesTaxCalculatorServiceIfc shopping = new SalesTaxCalculatorServiceImpl();
                                                                       
                                                                        // calculate data
                                                                        shopping.calculateTax(shoppingCartList);

                                                            } catch (Throwable e) {
                                                                        // This operation is not supported hence getting an error
                                                                        System.out.println(e.getLocalizedMessage());
                                                            }
                                                        }

                                                        /**
                                                         * Method purpose :
                                                         *  Input 3
                                                         *  1 imported bottle of perfume at 27.99 1 bottle of perfume at 18.99
                                                         *  1 packet of headache pills at 9.75 1 box of imported chocolates at 11.25
                                                         *   Insert calculate tax
                                                         *
                                                         * @param
                                                         * @return void
                                                         */

                                                        public void testScenarioInput_003() {

                                                            InputDataDTO inputDataDTO = null;
                                                            ArrayList<InputDataDTO> shoppingCartList = new ArrayList<InputDataDTO>();
                                                            //System.out.println("Input 3:");
                                                            // TODO Input 3 :
                                                            // 1 imported bottle of perfume at 27.99
                                                            inputDataDTO = new InputDataDTO();
                                                            inputDataDTO.setGoodsName("Imported bottle of perfume");
                                                            inputDataDTO.setGoodsquontity(1);
                                                            inputDataDTO.setGoodsPrice(new BigDecimal(27.99));
                                                            inputDataDTO.setDomesticTaxPercentage(true);
                                                            inputDataDTO.setAdditionalImportedTaxPercentage(true);
                                                            //TODO System.out.println(inputDataDTO.toString());
                                                            //Add to List
                                                            shoppingCartList.add(inputDataDTO);

                                                            // 1 bottle of perfume at 18.99
                                                            inputDataDTO = new InputDataDTO();
                                                            inputDataDTO.setGoodsName("Bottle of perfume");
                                                            inputDataDTO.setGoodsquontity(1);
                                                            inputDataDTO.setGoodsPrice(new BigDecimal(18.99));
                                                            inputDataDTO.setDomesticTaxPercentage(true);
                                                            //TODO System.out.println(inputDataDTO.toString());
                                                            //Add to list
                                                            shoppingCartList.add(inputDataDTO);

                                                            // 1 packet of headache pills at 9.75
                                                            inputDataDTO = new InputDataDTO();
                                                            inputDataDTO.setGoodsName("Packet of headache pills");
                                                            inputDataDTO.setGoodsquontity(1);
                                                            inputDataDTO.setGoodsPrice(new BigDecimal(9.75));
                                                            inputDataDTO.setExemptTax(true);
                                                            //TODO System.out.println(inputDataDTO.toString());
                                                            //Add to list
                                                            shoppingCartList.add(inputDataDTO);

                                                            // 1 box of imported chocolates at 11.25
                                                            inputDataDTO = new InputDataDTO();
                                                            inputDataDTO.setGoodsName("Imported box of chocolates");
                                                            inputDataDTO.setGoodsquontity(1);
                                                            inputDataDTO.setGoodsPrice(new BigDecimal(11.25));
                                                            inputDataDTO.setAdditionalImportedTaxPercentage(true);
                                                            //TODO System.out.println(inputDataDTO.toString());
                                                            //Add to list
                                                            shoppingCartList.add(inputDataDTO);
                                                            try {
                                                                        // Creating the ShoppingCart
                                                                        SalesTaxCalculatorServiceIfc shopping = new SalesTaxCalculatorServiceImpl();
                                                                       
                                                                        // calculate data
                                                                        shopping.calculateTax(shoppingCartList);

                                                            } catch (Throwable e) {
                                                                        // This operation is not supported hence getting an error
                                                                        System.out.println(e.getLocalizedMessage());
                                                            }
                                                        }
                                                       
                                                       
}

Input 1:
   1 book at 12.49
   1 music CD at 14.99
   1 chocolate bar at 0.85
                                                         

 Input 2:
  1 imported box of chocolates at 10.00
  1 imported bottle of perfume at 47.50
                                                       
 Input 3
  1 imported bottle of perfume at 27.99 1 bottle of perfume at 18.99
  1 packet of headache pills at 9.75 1 box of imported chocolates at 11.25

and display following output             

  
                             :SHOPPING BILL:


 =========================================================================
Quantity                                     Name                            Price
=========================================================================
1                      Book                             :12.50
1                      Music CD                      :16.50
1                      Chocolate bar               :0.85
-------------------------------------------------------------------------
Sales Tax : 1.50
Total : 29.85
=========================================================================
                                   

                                               :SHOPPING BILL:


=========================================================================
Quantity                                     Name                            Price
=========================================================================
1                      Imported box of chocolates                   :10.50
1                      Imported bottle of perfume                           :54.65
--------------------------------------------------------------------------------------------
Sales Tax : 7.65
Total : 65.15
=========================================================================
                                   
                                                 :SHOPPING BILL:


=========================================================================
Quantity                                     Name                            Price
=========================================================================
1                      Imported bottle of perfume                    :32.20
1                      Bottle of perfume                                   :20.90
1                      Packet of headache pills                       :9.75
1                      Imported box of chocolates                   :11.85
----------------------------------------------------------------------------
Sales Tax : 6.70
Total : 74.65
=========================================================================
  
2)SalesTaxCalculatorServiceIfc.java

This is single point of contact to outside world , its contract for ServiceImpl and its kind of facade for user.

package com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.service;

import java.util.List;

public interface SalesTaxCalculatorServiceIfc {

      
       /**
        * Calculate Shopping Cart Bill Users calculate records Bill (Once bill gets
        * generate user can’t add, edit, remove or calculate records.
        */
       public void calculateTax(List<?> shopingCartInputDTOList) ;
      
}


3) SalesTaxCalculatorServiceImpl.java :
This class implement SalesTaxCalculatorServiceIfc and contain following responsibility

1)Creating the ShoppingCart object using static factory
2) give call to Bo  methods
3) Handle exception if any exception would arise.
package com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.service.impl;

import java.util.List;

import com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.bo.SalesTaxCalculatorIfc;
import com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.exception.ShopingCartException;
import com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.factory.ShopingCartFactory;
import com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.service.SalesTaxCalculatorServiceIfc;
import com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.util.ShoppingCartconstantsIfc;
/**
 * following class use for single point of contact of external world
 * @author Vaquar Khan
 *
 */
public class SalesTaxCalculatorServiceImpl implements SalesTaxCalculatorServiceIfc {

                                                        /**
                                                         * This is only point of contact of outside world Calculate Shopping Cart
                                                         * Bill Users calculate records Bill (Once bill gets generate user can’t
                                                         * add, edit, remove or calculate records.
                                                         * @throws ShopingCartException
                                                         */
                                                        public void calculateTax(List<?> shopingCartInputDTOList)  {
                                                            SalesTaxCalculatorIfc selesTaxCalculatorIfc = null;

                                                            // Creating the ShoppingCart object using static factory
                                                            selesTaxCalculatorIfc = ShopingCartFactory.getInstance(ShoppingCartconstantsIfc.SERVICE_TAXCALCULATORSERVICEIMPL);

                                                            // calculate data
                                                            try {
                                                                        selesTaxCalculatorIfc.calculateTax(shopingCartInputDTOList);
                                                            } catch (ShopingCartException e ) {
                                                                        // TODO Auto-generated catch block
                                                                        e.printStackTrace();
                                                                        //Logs exception into logging
                                                            }

                                                        }

}


4) SalesTaxCalculatorIfc.java
its contract for SalesTaxCalculatorBaseImpl 

package com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.bo;

import java.util.List;

import com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.dto.impl.InputDataDTO;
import com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.exception.ShopingCartException;
/**
 *
 * @author Vaquar Khan
 *
 */
public interface SalesTaxCalculatorIfc {
                                                       
                                                        /**
                                                         * following class is single point of entry of outside world
                                                         * @param shopingCartInputDTOList
                                                         * @throws ShopingCartException
                                                         */
                                                        public void calculateTax(List<?> shopingCartInputDTOList) throws ShopingCartException;
                                                        /**
                                                         * Displaying result as per user requirement
                                                         * @param shopingCartInputDTOList
                                                         */
                                                        public  void displayBill(List<?> shopingCartInputDTOList);
                                                        /**
                                                         * calculate tax
                                                         * @param shopingCartInputDTOList
                                                         * @return
                                                         * @throws ShopingCartException
                                                         */
                                                        public  List<InputDataDTO> calculateTaxPercentage(List<?> shopingCartInputDTOList) throws ShopingCartException ;

}

5) SalesTaxCalculatorBaseImpl.java
This is abstract class , i am using decorator pattern here so it would give few abstract method for concrete decorator and its open for future change.

package com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.bo.impl;

import java.math.BigDecimal;
import java.util.Iterator;
import java.util.List;

import com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.bo.SalesTaxCalculatorIfc;
import com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.dto.impl.InputDataDTO;
import com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.exception.ShopingCartException;
/**
 * Following class in base class its contain few abstract methods
 * incase fure we need to make any changes , we can write new implementation .
 * =============================================================================
 * 1) Basic sales tax is applicable at a rate of 10% on all goods,
 * 2) Except books, food, and medical products that are exempt.
 * 3) Import duty is an  additional sales tax applicable on all imported goods at a rate of 5%, with no exemptions.
 *
 * When I purchase items I receive a receipt which lists the name of all the items and their price (including tax),
 *
 * finishing with the total cost of the items, and the total amounts of sales
 * taxes paid. The rounding rules for sales tax are that for a tax rate of n%, a
 * shelf price of p contains (np/100 rounded up to the nearest 0.05) amount of
 * sales tax.
 *
 * Write an application that prints out the receipt details for these shopping baskets...
 *
 * INPUT:
 *
 * Input 1: 1 book at 12.49 1 music CD at 14.99 1 chocolate bar at 0.85
 *
 * Input 2: 1 im@author Vaquar Khanported box of chocolates at 10.00 1 imported bottle of perfume
 * at 47.50
 *
 * Input 3: 1 imported bottle of perfume at 27.99 1 bottle of perfume at 18.99 1
 * packet of headache pills at 9.75 1 box of imported chocolates at 11.25
 *
 * OUTPUT
 *
 * Output 1: 1 book: 12.49 1 music CD: 16.49 1 chocolate bar: 0.85 Sales Taxes:
 * 1.50 Total: 29.83
 *
 * Output 2: 1 imported box of chocolates: 10.50 1 imported bottle of perfume:
 * 54.65 Sales Taxes: 7.65 Total: 65.15
 *
 * Output 3: 1 imported bottle of perfume: 32.19 1 bottle of perfume: 20.89 1
 * packet of headache pills: 9.75 1 imported box of chocolates: 11.85 Sales
 * Taxes: 6.70 Total: 74.68
 * =============================================================================
 *
 * @author Vaquar Khan
 */
public abstract class SalesTaxCalculatorBaseImpl implements SalesTaxCalculatorIfc {
    //ROUND_FACTOR
                                                        private static final BigDecimal ROUND_FACTOR = new BigDecimal("0.05");

                                                        /**
                                                         * Calculate Shopping Cart Bill Users calculate records Bill
                                                         * (Once bill gets generate user can’t add, edit, remove or calculate records)
                                                         * @throws ShopingCartException
                                                         */
                                                        public void calculateTax(List<?> shopingCartInputDTOList) throws ShopingCartException {

                                                            // Calculate records
                                                            List<?> newShopingCartInputDTOList = this.calculateTaxPercentage(shopingCartInputDTOList);
                                                            System.out.println(" \t \t \t :SHOPPING BILL: ");
                                                            System.out.println("\n \n ");
                                                            this.displayBill(newShopingCartInputDTOList);

                                                        }

                                                        /**
                                                         * Following method is used to display Bill
                                                         * May be in future requirement gets change and want to display result in different format thats why
                                                         * I have made it abstract
                                                         *
                                                         * @param shopingCartInputDTOList
                                                         */
                                                        public abstract void displayBill(List<?> shopingCartInputDTOList);

                                                        /**
                                                         * Following method is used to calculate tax
                                                         *  1) Basic sales tax is applicable at a rate of 10% on all goods,
                                                         *  2) Except books, food, and medical products that are exempt.
                                                         *  3) Import duty is an additional sales
                                                         * tax applicable on all imported goods at a rate of 5%, with no exemptions.
                                                         *
                                                         * When I purchase items I receive a receipt which lists the name of all the
                                                         * items and their price (including tax),
                                                         *
                                                         * finishing with the total cost of the items, and the total amounts of
                                                         * sales taxes paid. The rounding rules for sales tax are that for a tax
                                                         * rate of n%, a shelf price of p contains (np/100 rounded up to the nearest
                                                         * 0.05) amount of sales tax.
                                                         *
                                                         * @param shopingCartInputDTOList
                                                         * @return
                                                         * @throws ShopingCartException
                                                         */
                                                        public abstract List<InputDataDTO> calculateTaxPercentage(
                                                                        List<?> shopingCartInputDTOList) throws ShopingCartException;

                                                        /**
                                                         * Following method is used for add tax with price
                                                         *
                                                         * @param calculateTaxPercentage
                                                         * @param goodsPrice
                                                         * @return
                                                         */
                                                        protected BigDecimal addTaxandPrice(BigDecimal calculateTaxPercentage,
                                                                        BigDecimal goodsPrice) {
                                                            BigDecimal addtax = calculateTaxPercentage.add(goodsPrice);
                                                            addtax = roundOff(addtax);
                                                            return addtax;
                                                        }

                                                        /**
                                                         * Following method is used to calculate total price
                                                         *
                                                         * @param shopingCartInputDTOList
                                                         * @return
                                                         */
                                                        protected BigDecimal totalPrice(List<?> shopingCartInputDTOList) {

                                                            BigDecimal total = new BigDecimal(0);

                                                            if (shopingCartInputDTOList != null) {
                                                                        Iterator<?> shopingCartInputDTOListItr = shopingCartInputDTOList
                                                                                                .iterator();

                                                                        while (shopingCartInputDTOListItr.hasNext()) {
                                                                                    InputDataDTO inputDataDTO = (InputDataDTO) shopingCartInputDTOListItr
                                                                                                            .next();
                                                                                    total = total.add(inputDataDTO.getGoodsPrice().add(inputDataDTO.getCalculateTaxPercentage()));

                                                                        }// end of while

                                                            }
                                                            total = this.roundOff(total);

                                                            return total;
                                                        }

                                                        /**
                                                         * Following method is used to calculate total tax
                                                         *
                                                         * @param shopingCartInputDTOList
                                                         * @return
                                                         */
                                                        protected BigDecimal totalTax(List<?> shopingCartInputDTOList) {

                                                            BigDecimal total = new BigDecimal(0);

                                                            if (shopingCartInputDTOList != null) {
                                                                        Iterator<?> shopingCartInputDTOListItr = shopingCartInputDTOList.iterator();

                                                                        while (shopingCartInputDTOListItr.hasNext()) {
                                                                                    InputDataDTO inputDataDTO = (InputDataDTO) shopingCartInputDTOListItr.next();
                                                                                    total = total.add(inputDataDTO.getCalculateTaxPercentage());
                                                                        }// end of while

                                                                        total = roundOff(total);
                                                            }
                                                            return total;
                                                        }

                                                        /**
                                                         * JDK 1.4 Following method is used for round off up to .05 Round of 0.05
                                                         *
                                                         * @param value
                                                         * @return
                                                         */

                                                        protected BigDecimal roundOff(BigDecimal value) {
                                                            value = value.divide(ROUND_FACTOR);
                                                            value = new BigDecimal(Math.ceil(value.doubleValue()));
                                                            value = value.multiply(ROUND_FACTOR);
                                                            return value;
                                                        }

}

6) SalesTaxCalculatorImpl.java
This class extends SalesTaxCalculatorBaseImpl and provide implementation of abstract method .


in future if user want to calculate in diffrent way
public  List<InputDataDTO> calculateTaxPercentage(List<?> shopingCartInputDTOList) throws ShopingCartException;

in future if user want to see different format
public  void displayBill(List<?> shopingCartInputDTOList);


package com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.bo.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.dto.impl.InputDataDTO;
import com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.exception.ShopingCartException;
import com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.util.ShoppingCartconstantsIfc;
import com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.util.Util;

public class SalesTaxCalculatorImpl extends SalesTaxCalculatorBaseImpl{
                                                       
                                                       
                                                        /**
                                                         * @author Vaquar Khan
                                                         * Following method is used to calculate tax
                                                         * 1) Basic sales tax is applicable at a rate of 10% on all goods,
                                                         * 2) Except books, food, and  medical products that are exempt.
                                                         * 3) Import duty is an additional sales  tax applicable on all imported goods at a rate of 5%, with no exemptions.
                                                         *
                                                         * When I purchase items I receive a receipt which lists the name of all the
                                                         * items and their price (including tax),
                                                         *
                                                         * finishing with the total cost of the items, and the total amounts of
                                                         * sales taxes paid. The rounding rules for sales tax are that for a tax
                                                         * rate of n%, a shelf price of p contains (np/100 rounded up to the nearest
                                                         * 0.05) amount of sales tax.
                                                         *
                                                         * @param shopingCartInputDTOList
                                                         * @return
                                                         * @throws ShopingCartException
                                                         */
                                                        public  List<InputDataDTO> calculateTaxPercentage(List<?> shopingCartInputDTOList) throws ShopingCartException {

                                                            BigDecimal salesTax = null;
                                                            BigDecimal hundread = new BigDecimal(ShoppingCartconstantsIfc.HUNDREAD);
                                                            BigDecimal taxPercentage = null;

                                                            List<InputDataDTO> newShopingCartInputDTOList = new ArrayList<InputDataDTO>();

                                                            if (shopingCartInputDTOList != null) {
                                                                        //Create Iterator
                                                                        Iterator<?> shopingCartInputDTOListItr = shopingCartInputDTOList.iterator();
                                                                       
                                                                        while (shopingCartInputDTOListItr.hasNext()) {
                                                                                    InputDataDTO inputDataDTO = (InputDataDTO) shopingCartInputDTOListItr.next();

                                                                                    if (inputDataDTO.isExemptTax()) {
                                                                                                taxPercentage = new BigDecimal(Util.readProperties(ShoppingCartconstantsIfc.EXEMPT));
                                                                                    } else if (inputDataDTO.isDomesticTaxPercentage()&& inputDataDTO.isAdditionalImportedTaxPercentage()) {
                                                                                                taxPercentage = new BigDecimal(Util.readProperties(ShoppingCartconstantsIfc.BASIC_SALES_TAX_AND_IMPORT_DUTY));
                                                                                    } else if (inputDataDTO.isDomesticTaxPercentage()) {
                                                                                                taxPercentage = new BigDecimal(Util.readProperties(ShoppingCartconstantsIfc.BASIC_SALES_TAX));
                                                                                    } else if (inputDataDTO.isAdditionalImportedTaxPercentage()) {
                                                                                                taxPercentage = new BigDecimal(Util.readProperties(ShoppingCartconstantsIfc.IMPORT_DUTY));
                                                                                    }

                                                                                    // Calculate Tax
                                                                                    salesTax = inputDataDTO.getGoodsPrice().multiply(taxPercentage).divide(hundread);
                                                                                    //created new dto list
                                                                                                                                newShopingCartInputDTOList=Util.convertDto(inputDataDTO,salesTax, newShopingCartInputDTOList);
                                                                                    // put values into Map
                                                                        }// While

                                                            }
                                                            return newShopingCartInputDTOList;
                                                        }

                                                        /**Following method is used to display Bill
                                                         *
                                                         * @param shopingCartInputDTOList
                                                         */
                                                        public  void displayBill(List<?> shopingCartInputDTOList) {

                                                            if (shopingCartInputDTOList != null) {
                                                                        Iterator<?> shopingCartInputDTOListItr = shopingCartInputDTOList
                                                                                                .iterator();

                                                                        //System.out.println(" \t \t \t :SHOPPING BILL: ");
                                                                        //System.out.println("\n \n ");
                                                                        System.out
                                                                                                                                            .println("=============================================================================");
                                                                        System.out.println("Quantity " + "\t" + "\t" + "\t" + " Name "
                                                                                                + "\t" + "\t" + "\t" + " Price ");
                                                                        System.out
                                                                                                                                            .println("=============================================================================");

                                                                        while (shopingCartInputDTOListItr.hasNext()) {
                                                                                    InputDataDTO inputDataDTO = (InputDataDTO) shopingCartInputDTOListItr
                                                                                                            .next();

                                                                                    System.out.println(inputDataDTO.getGoodsquontity()
                                                                                                            + "\t"
                                                                                                            + "\t"
                                                                                                            + inputDataDTO.getGoodsName()
                                                                                                            + "\t"
                                                                                                            + "\t"
                                                                                                            + ":"
                                                                                                            + addTaxandPrice(inputDataDTO
                                                                                                                                    .getCalculateTaxPercentage(), inputDataDTO
                                                                                                                                    .getGoodsPrice()));

                                                                        }// While

                                                                        System.out
                                                                                                .println("----------------------------------------------------------------------------");
                                                                        System.out.println("Sales Tax : "
                                                                                                + totalTax(shopingCartInputDTOList));
                                                                        System.out
                                                                                                .println("Total : " + totalPrice(shopingCartInputDTOList));

                                                            }// end of if
                                                            System.out
                                                                                                                                .println("=============================================================================");
                                                        }

}


7) ShopingCartFactory.java

This class is using static factory pattern  with singleton pattern
package com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.factory;

import com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.bo.SalesTaxCalculatorIfc;
import com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.bo.impl.SalesTaxCalculatorImpl;
import com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.util.ShoppingCartconstantsIfc;

/**
 * This is Static Factory for Object creation. Currently its creating only
 * ServiceTaxCalculatorImpl object but if in future required to decorate any
 * object then factory would help it modify it easily.
 *
 * @author Vaquar Khan
 *
 */
public class ShopingCartFactory {

                                                        private static SalesTaxCalculatorIfc uniqueInstance = null;

                                                        // made class immutable
                                                        private ShopingCartFactory() {
                                                        }

                                                        /**
                                                         * following method will return instance of BO
                                                         * Currently single thread is accessing instance so no need synchronization
                                                         * @param type
                                                         * @return
                                                         */
                                                        public static SalesTaxCalculatorIfc getInstance(String type) {

                                                            // null check
                                                            if (type == null) {
                                                                        return uniqueInstance;
                                                            }

                                                            switch (type) {
                                                            case ShoppingCartconstantsIfc.SERVICE_TAXCALCULATORSERVICEIMPL:
                                                                        if (isUniqueInstance(uniqueInstance))
                                                                                    uniqueInstance = new SalesTaxCalculatorImpl();
                                                                        break;
                                                            // for future enhancement added only for example purpose not in use
                                                            case "ABC":
                                                                        if (isUniqueInstance(uniqueInstance))
                                                                        //TODO uniqueInstance = null;
                                                                        break;
                                                            // Default value
                                                            default:
                                                                        if (isUniqueInstance(uniqueInstance))
                                                                        uniqueInstance = new SalesTaxCalculatorImpl();
                                                                        break;
                                                            }

                                                            return uniqueInstance;
                                                        }
/**
 * Method is use instance null or not for readability purpose
 * @param Instance
 * @return
 */
                                                        private static boolean isUniqueInstance(SalesTaxCalculatorIfc Instance) {
                                                           
                                                            if (Instance == null)
                                                                        return true;
                                                            else
                                                                        return false;

                                                        }

}

8) ShopingCartException.java

This class is using if any exception arise , its shopping cart own exception

package com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.exception;
/**
 * ShopingCartException use for throw error if any issue found in ShopingCart project
 * @author Vaquar Khan
 *
 */
public class ShopingCartException extends Exception {

      

       String mistake;

       // Default constructor - initializes instance variable to unknown
       public ShopingCartException() {
              super(); // call superclass constructor
              mistake = "unknown";
       }

       // Constructor receives some kind of message that is saved in an instance
       // variable.
       public ShopingCartException(String err) {
              super(err); // call super class constructor
              mistake = err; // save message
       }

       // public method, callable by exception catcher. It returns the error
       // message.
       public String getError() {
              return mistake;
       }
}


9) ShoppingCartconstantsIfc.java
This class contain all constants

package com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.util;
/**
 * Following interface use for constants
 * @author Vaquar Khan
 *
 */
public interface ShoppingCartconstantsIfc {
      
      
      
       //Properties file location
       public String PROPERTIES_URL"salestaxrules.properties";
       //Properties File
       public String  EXEMPT ="EXEMPT";//exempt
       public String  IMPORT_DUTY = "IMPORTDUTY";//additionalImportedTaxPercentage
       public String BASIC_SALES_TAX = "BASICSALESTAX";//domesticTaxPercentage
       public String BASIC_SALES_TAX_AND_IMPORT_DUTY = "SALESTAXANDIMPORTDUTY";//additionalImportedTaxPercentage+domesticTaxPercentage
       //Service locator
       public String  SERVICE_TAXCALCULATORSERVICEIMPL"TaxCalculatorServiceImpl";
      
       public String HUNDREAD ="100";
      
      
}



10) Util.java
This class contain common utility methods

package com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.util;

import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.List;
import java.util.Properties;

import com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.dto.impl.InputDataDTO;
import com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.exception.ShopingCartException;

/**
 * Following class contains utility methods
 * @author Vaquar Khan
 *
 */
public class Util {

                                                           
                                                        /**
                                                         * Following method is used to read properties file
                                                         * read properties file
                                                         * @param keyValue
                                                         * @return
                                                         * @throws ShopingCartException
                                                         */
                                                        public static String readProperties(String keyValue) throws ShopingCartException {
                                                            Properties prop = new Properties();
                                                            FileInputStream fis=null;
                                                            String propertiesKey = null;
                                                            try {
                                                                        fis = new FileInputStream(
                                                                                                ShoppingCartconstantsIfc.PROPERTIES_URL);
                                                                        prop.load(fis);
                                                                        // get value from key
                                                                        propertiesKey = prop.getProperty(keyValue);
                                                                       
                                                            } catch (IOException e) {
                                                                        e.printStackTrace();
                                                            }finally{
                                                                        try {
                                                                                    fis.close();
                                                                        } catch (IOException e) {
                                                                                    // TODO Will handle Try.catch writing my own exception handler
                                                                                    e.printStackTrace();
                                                                                    throw new ShopingCartException("Issue in reading Filet");
                                                                        }
                                                            }
                                                            return propertiesKey;
                                                        }
/**
 * Following method is using to set result into InputDataDTO object
 *
 * @param inputDataDTO
 * @param salesTax
 * @return
 */
                                                        public static List<InputDataDTO> convertDto(InputDataDTO inputDataDTO,
                                                                        BigDecimal salesTax,List<InputDataDTO> newShopingCartInputDTOList) {

                                                            InputDataDTO newInputDataDTO = new InputDataDTO();
                                                           
                                                            newInputDataDTO.setAdditionalImportedTaxPercentage(inputDataDTO                                                        .isAdditionalImportedTaxPercentage());
                                                            newInputDataDTO.setCalculateTaxPercentage(salesTax);
                                                                                                                newInputDataDTO.setDomesticTaxPercentage(inputDataDTO.isDomesticTaxPercentage());
                                                            newInputDataDTO.setExemptTax(inputDataDTO.isExemptTax());
                                                            newInputDataDTO.setGoodsName(inputDataDTO.getGoodsName());
                                                            newInputDataDTO.setGoodsPrice(inputDataDTO.getGoodsPrice());
                                                            newInputDataDTO.setGoodsquontity(inputDataDTO.getGoodsquontity());
                                                            // Added into List
                                                            newShopingCartInputDTOList.add(newInputDataDTO);

                                                            return newShopingCartInputDTOList;
                                                        }
}








11) InputDataDTOIfc.java
This class is contract for InputDataDTO
package com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.dto;

import java.math.BigDecimal;

/**
 * Following Interface is using as a contract for DTO
 * @author Vaquar Khan
 *
 */
public interface InputDataDTOIfc {

       /**
        * Following method will use for isAdditionalImportedTaxPercentage
        *
        * @return
        */
       public boolean isAdditionalImportedTaxPercentage();

       /**
        * Following method will use for setAdditionalImportedTaxPercentage
        *
        * @param additionalImportedTaxPercentage
        */
       public void setAdditionalImportedTaxPercentage(
                     boolean additionalImportedTaxPercentage);

       /**
        * Following method will use for isDomesticTaxPercentage
        *
        * @return
        */

       public boolean isDomesticTaxPercentage();

       /**
        * Following method will use for setDomesticTaxPercentage
        *
        * @param domesticTaxPercentage
        */
       public void setDomesticTaxPercentage(boolean domesticTaxPercentage);

       /**
        * Following method will use for isExemptTax
        *
        * @return
        */
       public boolean isExemptTax();

       /**
        * Following method will use for setExemptTax
        *
        * @param exemptTax
        */
       public void setExemptTax(boolean exemptTax);

       /**
        * Following method will use for getGoodsName
        *
        * @return
        */
       public String getGoodsName();

       /**
        * Following method will use for setGoodsName
        *
        * @param goodsName
        */
       public void setGoodsName(String goodsName);

       /**
        * Following method will use for getGoodsPrice
        *
        * @return
        */
       public BigDecimal getGoodsPrice();

       /**
        * Following method will use for setGoodsPrice
        *
        * @param goodsPrice
        */
       public void setGoodsPrice(BigDecimal goodsPrice);

       /**
        * Following method will use for getGoodsquontity
        *
        * @return
        */
       public int getGoodsquontity();

       /**
        * Following method will use for setGoodsquontity
        *
        * @param goodsquontity
        */
       public void setGoodsquontity(int goodsquontity);

       /**
        * Following method will use for getCalculateTaxPercentage
        *
        * @return
        */
       public BigDecimal getCalculateTaxPercentage();

       /**
        * Following method will use for setCalculateTaxPercentage
        *
        * @param calculateTaxPercentage
        */
       public void setCalculateTaxPercentage(BigDecimal calculateTaxPercentage);

}


12) InputDataDTO.java
This class is using for Data transfer object its contain simple getter and setter
package com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.dto.impl;

import java.math.BigDecimal;

import com.thoughtworks.india.shoppingcart.salestax.java.calculatetax.dto.InputDataDTOIfc;

/**
 * following class is use as data transfer object
 * @author Vaquar Khan
 *
 */
public class InputDataDTO implements InputDataDTOIfc {

                                                        private boolean domesticTaxPercentage = false;
                                                        private boolean additionalImportedTaxPercentage = false;
                                                        private boolean exemptTax = false;
                                                        private String goodsName = "";
                                                        private BigDecimal goodsPrice = new BigDecimal(0);
                                                        private int goodsquontity = 0;
                                                        private BigDecimal calculateTaxPercentage = new BigDecimal(0);

                                                        /**
                                                         *
                                                         * @return
                                                         */
                                                        public boolean isAdditionalImportedTaxPercentage() {
                                                            return additionalImportedTaxPercentage;
                                                        }

                                                        /**
                                                         *
                                                         * @param additionalImportedTaxPercentage
                                                         */
                                                        public void setAdditionalImportedTaxPercentage(
                                                                        boolean additionalImportedTaxPercentage) {
                                                            this.additionalImportedTaxPercentage = additionalImportedTaxPercentage;
                                                        }

                                                        /**
                                                         *
                                                         * @return
                                                         */

                                                        public boolean isDomesticTaxPercentage() {
                                                            return domesticTaxPercentage;
                                                        }

                                                        /**
                                                         *
                                                         * @param domesticTaxPercentage
                                                         */
                                                        public void setDomesticTaxPercentage(boolean domesticTaxPercentage) {
                                                            this.domesticTaxPercentage = domesticTaxPercentage;
                                                        }

                                                        public boolean isExemptTax() {
                                                            return exemptTax;
                                                        }

                                                        /**
                                                         *
                                                         * @param exemptTax
                                                         */
                                                        public void setExemptTax(boolean exemptTax) {
                                                            this.exemptTax = exemptTax;
                                                        }

                                                        /**
                                                         *
                                                         * @return
                                                         */
                                                        public String getGoodsName() {
                                                            return goodsName;
                                                        }

                                                        /**
                                                         *
                                                         * @param goodsName
                                                         */
                                                        public void setGoodsName(String goodsName) {
                                                            this.goodsName = goodsName;
                                                        }

                                                        /**
                                                         *
                                                         * @return
                                                         */
                                                        public BigDecimal getGoodsPrice() {
                                                            return goodsPrice;
                                                        }

                                                        /**
                                                         *
                                                         * @param goodsPrice
                                                         */
                                                        public void setGoodsPrice(BigDecimal goodsPrice) {
                                                            this.goodsPrice = goodsPrice;
                                                        }

                                                        /**
                                                         *
                                                         * @return
                                                         */
                                                        public int getGoodsquontity() {
                                                            return goodsquontity;
                                                        }

                                                        /**
                                                         *
                                                         * @param goodsquontity
                                                         */
                                                        public void setGoodsquontity(int goodsquontity) {
                                                            this.goodsquontity = goodsquontity;
                                                        }

                                                        /**
                                                         *
                                                         * @return
                                                         */
                                                        public BigDecimal getCalculateTaxPercentage() {
                                                            return calculateTaxPercentage;
                                                        }

                                                        /**
                                                         *
                                                         * @param calculateTaxPercentage
                                                         */
                                                        public void setCalculateTaxPercentage(BigDecimal calculateTaxPercentage) {
                                                            this.calculateTaxPercentage = calculateTaxPercentage;
                                                        }

@Override
public String toString() {
                                                        System.out.println("Input Data ");
                                                        System.out.println("----------------------------------------------------------------------------------------------------------- ");
                                                        return "\n\n "+"Goods Name="+ getGoodsName()+"\n "+"Goods Quontity="+getGoodsquontity()+"\n "+"Good sPrice="+getGoodsPrice();
}
}


13) salestaxrules.properties

This is properties file contain frequently change value

## exempt item tax  %
EXEMPT=0
## Import duty item tax %
IMPORTDUTY=5
##Basic sales tax %
BASICSALESTAX=10
##Basic sales tax +Import duty %
SALESTAXANDIMPORTDUTY=15


## Properties File
## Following properties file contain values which would change in future
## We can change any value without touch our code and it will return result as per requirements
##--------------------
## exempt item tax  %
EXEMPT=0
## Import duty item tax %
IMPORTDUTY=5
##Basic sales tax %
BASICSALESTAX=10
##Basic sales tax +Import duty %
SALESTAXANDIMPORTDUTY=15

Soon will share with you few more design problems

No comments:

Post a Comment