概要
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% |