19
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

概要

2023年10月よりインボイス制度が開始されました。

それにより、これまで税込金額で取り扱われていたシステムに対して、
「消費税額」を計算する必要が出てきました。

ちょうど仕事柄、税込金額から消費税の計算をする必要ができましたので、
消費税の計算プログラムを作ってみました。

計算プログラム


<?php

class Consumption_tax_target
{
    // 計算対象のデータを保持する配列
    private array $consumption_tax_target_list = [];

    public function __construct()
    {

    }

    /**
     * @param int $price
     * @param float $consumption_tax_rate
     *
     * @return void
     */
    public function add(int $price, float $consumption_tax_rate): void
    {
        $this->consumption_tax_target_list[] = [
            'target_consumption_tax_rate' => $consumption_tax_rate,
            'price' => $price,
        ];
    }

    /**
     * @return array
     */
    public function get(): array
    {
        return $this->consumption_tax_target_list;
    }
}


class Consumption_tax_group
{
    // 税率ベースの配列
    private array $consumption_tax_group = [];

    public function __construct()
    {
        $this->initialize();
    }

    /**
     * 税率ベースの配列を初期化する
     *
     * @return void
     */
    private function initialize(): void
    {
        foreach (Consumption_tax::RATES as $consumption_tax_rate) {
            $this->add_rate_group($consumption_tax_rate);
        }
    }

    /**
     * @param float $consumption_tax_rate
     *
     * @return void
     */
    private function add_rate_group(float $consumption_tax_rate): void
    {
        $this->consumption_tax_group[] = [
            'consumption_tax_rate' => $consumption_tax_rate,
            'total_price' => 0,
            'consumption_tax_price' => 0,
        ];
    }

    /**
     * @param float $target_consumption_tax_rate
     * @param int $price
     *
     * @return void
     */
    public function add_consumption_tax_group_price(float $target_consumption_tax_rate, int $price): void
    {
        $key = $this->get_consumption_tax_group_key($target_consumption_tax_rate);

        $this->consumption_tax_group[$key]['total_price'] += $price;
    }

    /**
     * @param float $target_consumption_tax_rate
     * @param int $price
     *
     * @return void
     */
    public function set_consumption_tax_price(float $target_consumption_tax_rate, int $price): void
    {
        $key = $this->get_consumption_tax_group_key($target_consumption_tax_rate);

        $this->consumption_tax_group[$key]['consumption_tax_price'] = $price;
    }

    /**
     * @param float $target_consumption_tax_rate
     *
     * @return int
     */
    private function get_consumption_tax_group_key(float $target_consumption_tax_rate): int
    {
        $key = array_search($target_consumption_tax_rate, array_column($this->consumption_tax_group, 'consumption_tax_rate'));

        // システム的に把握していない税率が指定された場合は動的に、税率グループを追加する
        if ($key === false) {
            $this->add_rate_group($target_consumption_tax_rate);
            $key = $this->get_consumption_tax_group_key($target_consumption_tax_rate);
        }

        return $key;
    }

    /**
     * @return array
     */
    public function get_consumption_tax_group(): array
    {
        return $this->consumption_tax_group;
    }

    /**
     * @param float $target_consumption_tax_rate
     *
     * @return int
     */
    public function get_target_consumption_tax_rate_price(float $target_consumption_tax_rate): int
    {
        $key = $this->get_consumption_tax_group_key($target_consumption_tax_rate);

       return $this->consumption_tax_group[$key]['consumption_tax_price'];
    }
}


class Consumption_tax_calculator
{
    private Consumption_tax_group $consumption_tax_group;
    private Consumption_tax_target $consumption_tax_target;

    private Consumption_tax_round_type_enum $round_type_enum;

    public function __construct(Consumption_tax_round_type_enum $round_type_enum)
    {
        $this->instantiation_of_consumption_tax_group();
        $this->instantiation_of_consumption_tax_target();

        $this->round_type_enum = $round_type_enum;
    }

    /**
     * @return void
     */
    private function instantiation_of_consumption_tax_group(): void
    {
        $this->consumption_tax_group = new Consumption_tax_group();
    }

    /**
     * @return void
     */
    private function instantiation_of_consumption_tax_target(): void
    {
        $this->consumption_tax_target = new Consumption_tax_target();
    }

    /**
     * @param int $price
     * @param float $consumption_tax_rate
     *
     * @return void
     */
    public function add_target(int $price, float $consumption_tax_rate): void
    {
        $this->consumption_tax_target->add($price, $consumption_tax_rate);

        $this->calculate_in_consumption_tax_group();
    }

    /**
     * 消費税の小数点を処理する。
     *
     * @param float $tax
     *
     * @return float
     */
    private function round_consumption_tax(float $tax): float
    {
        if ($this->round_type_enum->is_floor()) {
            return floor($tax);
        } elseif ($this->round_type_enum->is_ceil()) {
            return ceil($tax);
        } elseif ($this->round_type_enum->is_round()) {
            return round($tax);
        }
        return $tax;
    }

    /**
     * 金額から内税を算出して返却する
     *
     * @param integer $price
     * @param float $rate
     *
     * @return int
     */
    private function calculate_in_consumption_tax(int $price, float $rate): int
    {
        $rate_for_calc = (float)bcdiv($rate, 100, 2);
        $tax = (float)bcmul((float)bcdiv($price, (float)bcadd(1, $rate_for_calc, 2), 2), $rate_for_calc, 2);
        // NOTE: 外税の場合の計算 ($price * $rate_for_calc)
        return $this->round_consumption_tax($tax);
    }

    /**
     * @return void
     */
    private function calculate_in_consumption_tax_group(): void
    {
        $this->instantiation_of_consumption_tax_group();
        foreach ($this->get_consumption_tax_target() as $item) {
            $this->consumption_tax_group->add_consumption_tax_group_price($item['target_consumption_tax_rate'], $item['price']);
        }

        // 消費税率ごとの消費税を算出する
        foreach ($this->get_consumption_tax_group() as $item) {
            $consumption_tax_price = $this->calculate_in_consumption_tax($item['total_price'], $item['consumption_tax_rate']);

            $this->consumption_tax_group->set_consumption_tax_price($item['consumption_tax_rate'], $consumption_tax_price);
        }
    }

    /**
     * 全消費税率の消費税額を取得する
     *
     * @return int
     */
    public function get_summary_of_consumption_tax_price(): int
    {
        $consumption_tax_price = 0;
        foreach ($this->get_consumption_tax_group() as $item) {
            $consumption_tax_price += $item['consumption_tax_price'];
        }

        return $consumption_tax_price;
    }

    /**
     * @return array
     */
    public function get_consumption_tax_group(): array
    {
        return $this->consumption_tax_group->get_consumption_tax_group();
    }

    /**
     * @return array
     */
    public function get_consumption_tax_target(): array
    {
        return $this->consumption_tax_target->get();
    }

    /**
     * @param float $target_consumption_tax_rate
     *
     * @return int
     */
    public function get_target_consumption_tax_rate_price(float $target_consumption_tax_rate): int
    {
        return $this->consumption_tax_group->get_target_consumption_tax_rate_price($target_consumption_tax_rate);
    }
}

class Consumption_tax_round_type_enum
{
    private int $scalar;
    
    /** 切り捨て */
    public const FLOOR = 1;
    /** 切り上げ */
    public const CEIL  = 2;
    /** 四捨五入 */
    public const ROUND = 3;

    public const LABELS = [
        self::FLOOR => '切り捨て',
        self::CEIL  => '切り上げ',
        self::ROUND => '四捨五入',
    ];

    public function __construct(int $value)
    {
        $this->scalar = $value;
    }

    public function value(): int
    {
        return $this->scalar;
    }
    
    /**
     * @return integer
     */
    public static function get_default(): int
    {
        return self::FLOOR;
    }

    /**
     * @return array
     */
    public static function values(): array
    {
        return [ self::FLOOR, self::CEIL, self::ROUND ];
    }

    /**
     * 切り捨てである
     *
     * @return boolean
     */
    public function is_floor(): bool
    {
        return $this->value() == self::FLOOR;
    }

    /**
     * 切り上げである
     *
     * @return boolean
     */
    public function is_ceil(): bool
    {
        return $this->value() == self::CEIL;
    }

    /**
     * 四捨五入である
     *
     * @return boolean
     */
    public function is_round(): bool
    {
        return $this->value() == self::ROUND;
    }
}


final class Consumption_tax
{
    /** 消費税コード:非課税 */
    public const NONE = 0;
    /** 消費税コード:標準税率 */
    public const STANDARD = 1;
    /** 消費税コード:軽減税率 */
    public const REDUCED = 2;

    /** 消費税率 (パーセント) */
    public const RATES = [
        self::STANDARD => 10,
        self::REDUCED  => 8,
        self::NONE     => 0,
    ];
}


動かすコード


<?php

$round_type = new Consumption_tax_round_type_enum(Consumption_tax_round_type_enum::ROUND);
$consumption_tax_calculator = new Consumption_tax_calculator($round_type);
$consumption_tax_calculator->add_target(1000, 0);
$consumption_tax_calculator->add_target(10, 0);
$consumption_tax_calculator->add_target(2000, 10);
$consumption_tax_calculator->add_target(20, 10);
$consumption_tax_calculator->add_target(3000, 8);
$consumption_tax_calculator->add_target(30, 8);
$consumption_tax_calculator->add_target(1000, 3.5);

$consumption_tax_price = $consumption_tax_calculator->get_summary_of_consumption_tax_price();

echo $consumption_tax_price.PHP_EOL;
// 437

購入リスト

No 商品名 税込金額 税率
1 鉛筆10本 1,000円 0%
2 鉛筆1本 10円 0%
3 消しゴム10個 2,000円 10%
4 消しゴム1個 20円 10%
5 りんご10個 3,000円 8%
6 りんご1個 30円 8%
7 スマイル 1,000円 3.5%
19
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?