<?php
namespace Eccube\Service\PurchaseFlow\Processor;
use Eccube\Annotation\OrderFlow;
use Eccube\Entity\ItemHolderInterface;
use Eccube\Service\PurchaseFlow\PurchaseContext;
use Eccube\Service\PurchaseFlow\PurchaseProcessor;
use Eccube\Entity\Order;
use Plugin\CouponPro42\Entity\Master\CouponKind;
use Eccube\Repository\TaxRuleRepository;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Eccube\Service\TaxRuleService;
/**
* @OrderFlow
*/
class AgencyCommissionProcessor implements PurchaseProcessor
{
protected $taxRuleRepository;
/**
* @var TaxRuleService
*/
private $taxRuleService;
/**
* @var ContainerInterface
*/
protected $container;
public function __construct(
ContainerInterface $container,
TaxRuleRepository $taxRuleRepository,
TaxRuleService $taxRuleService
) {
$this->container = $container;
$this->taxRuleService = $taxRuleService;
$this->taxRuleRepository = $taxRuleRepository;
}
public function prepare(ItemHolderInterface $itemHolder, PurchaseContext $context)
{
// このメソッドは今回使用しない
error_log('prepare');
}
public function commit(ItemHolderInterface $itemHolder, PurchaseContext $context)
{
if (!$itemHolder instanceof Order) {
return;
}
// 代理店手数料の計算と設定を行う
$this->calculateAgencyCommission($itemHolder);
}
public function rollback(ItemHolderInterface $itemHolder, PurchaseContext $context)
{
// ロールバック処理が必要な場合に実装
error_log('rollback');
}
//代理店手数料の計算
private function calculateAgencyCommission(Order $Order)
{
// OrderItemを取得
$OrderItems = $Order->getOrderItems();
// 代理店手数料の計算と設定を行う
// デバック用 ログにクーポンタイプを記録
// OrderItemからOrderItemTypeエンティティを取得
// foreach ($OrderItems as $OrderItem) {
// $itemType = $OrderItem->getOrderItemType();
// error_log("一つのループ");
// error_log($itemType);
// $orderDiscountTotal = $Order->getDiscount();
// error_log($orderDiscountTotal);
// // クーポンによる値引き商品を特定
// if ($OrderItem->getPrice() < 0) {
// error_log("値引き項目");
// }
// }
//===========================
//カスタマイズ 代理店手数料の計算
//===========================
//受注作成時に代理店手数料金を計算して、データベースへ保存する
//代理店手数料の計算方法:商品価格*代理店手数料率(%)
//1.値引きクーポンがあった場合(クーポンタイプ1):代理店手数料率(%)が最も低い商品に対して値引きをした上で、代理店手数料を計算
//2.送料無料クーポンがあった場合(クーポンタイプ3):値引き額は考慮せずに代理店手数料を計算
//3.割引クーポンがあった場合(クーポンタイプ2):注文の代理店手数料の合計値に対して割引率を適用
//4(なし).定期受注の回数値引きがあった場合:対象の値引き商品に対して、回数値引きを適用した金額の代理店手数料金額を計算
//5(なし).定期受注の回数値引きがあり、さらにその商品が代理店手数料率が高くクーポンによる値引きがあった場合:対象の商品に対して、回数値引きを適用した金額を設定し、その金額の代理店手数料を計算
//※クーポン価格>対象商品価格の場合は、残価分が0になるまで次ループで処理
//--------------
//ここで購入者が会員だった場合、会員データベースのagency_codeの値を取得して、値がnullではなかった場合にその情報をorderデータベースのagency_order_codeカラムへ保存する
// OrderオブジェクトからCustomerオブジェクトを取得
$Customer = $Order->getCustomer();
// 会員データベースからagency_codeを取得
if ($Customer && $Customer->getId() && $Customer->getAgencyCode()) {
// 商品の総個数を計算するための変数を初期化
$totalProductQuantity = 0;
// 値引き額を初期化
// $discountAmount = 0;
// 代理店手数料率の初期化
$agencyCommissionRate = 0;
//================
//税設定の取得
//================
// TaxRuleRepositoryのインスタンスを取得
$taxRuleRepository = $this->container->get(TaxRuleRepository::class);
// 現在有効な税率設定を取得
$currentTaxRule = $taxRuleRepository->getByRule();
// 税率を取得
$taxRate = $currentTaxRule->getTaxRate();
// 現在有効な税率設定を取得
$taxRoundingType = $currentTaxRule->getRoundingType()->getId();
//調整値
$taxAdjust = $currentTaxRule->getTaxAdjust();
// 代理店手数料合計値の初期化
$totalAgencyCommission = 0;
//=================
//クーポンフラグの初期化
//=================
$flgDiscountCoupon = false; //値引きクーポンフラグ
$flgRebateCoupon = false; //割引クーポンフラグ
$flgPostageCoupon = false; //送料無料クーポンフラグ
//=================
//クーポン値引情報の初期化
//=================
$amountDiscountCoupon = 0; //クーポン値引金額
//=================
//クーポン値引金額の残高
//=================
$amountDiscountCouponBalance = 0;
//=================
//定期購入回数割引情報の初期化
//================
$discountDetails = [];
//デバッグ用
// error_log('代理店手数料のループ');
//=====================================
//代理店手数料率に応じて商品の配列の並び替えを実行
//=====================================
// 商品かつ代理店手数料が設定されている商品の配列
$agencyCommissionRates = [];
foreach ($OrderItems as $OrderItem) {
// 商品情報の取得
$ProductClass = $OrderItem->getProductClass();
if ($ProductClass) {
// 商品に設定された代理店手数料率(%)を取得
$agencyCommissionRate = $ProductClass->getAgencyCommission();
// 代理店手数料が取得できた場合のみ配列に格納
if ($agencyCommissionRate !== null) {
$agencyCommissionRates[] = ['orderItem' => $OrderItem, 'rate' => $agencyCommissionRate];
}
// 手数料が取得できなかった場合配列に入れない
// 0%があった場合の例外処理
else {
// $agencyCommissionRates[] = ['orderItem' => $OrderItem, 'rate' => 0];
}
}
// regularOrderDetails フィールドから定期割引詳細を取得
$regularOrderDetails = $OrderItem->getRegularOrderDetails();
// 各商品に対する定期割引情報が含まれている場合
if ($regularOrderDetails) {
error_log('各商品に対する定期割引情報が含まれている場合');
foreach ($regularOrderDetails as $detail) {
// 商品IDと割引価格を $discountDetails 配列に格納
$discountDetails[$detail['productId']] = $detail['discountPrice'];
error_log('定期割引' . $discountDetails[$detail['productId']]);
}
}
}
// 代理店手数料率でソート
usort($agencyCommissionRates, function ($a, $b) {
return $a['rate'] <=> $b['rate'];
});
// ソートされたOrderItemsを取得
$sortedOrderItems = array_map(function ($item) {
return $item['orderItem'];
}, $agencyCommissionRates);
//適用されているクーポンタイプの取得
foreach ($OrderItems as $OrderItem) {
//==============================
//商品に設定されたクーポンタイプの取得
//1:値引
//2:割引
//3:送料無料
//==============================
//クーポンタイプと値引き額の取得
$OrderItemCouponKind = $OrderItem->getCouponKind();
if ($OrderItemCouponKind) {
//値引きクーポンの場合
if ($OrderItemCouponKind == CouponKind::DISCOUNT_PRICE) {
$flgDiscountCoupon = true; //値引きクーポンフラグ
error_log('値引きクーポンフラグ 適用');
//割引クーポンの場合
} else if ($OrderItemCouponKind == CouponKind::DISCOUNT_RATE) {
$flgRebateCoupon = true; //割引クーポンフラグ
error_log('割引クーポンフラグ 適用');
}
//送料無料クーポンの場合
else if ($OrderItemCouponKind == CouponKind::FREE_DELIVERY_FEE) {
$flgPostageCoupon = true; //送料無料クーポンフラグ
error_log('送料クーポンフラグ 適用');
}
//値引額の取得
$amountDiscountCoupon = $OrderItem->getPrice();
//クーポン残価の更新
$amountDiscountCouponBalance = $amountDiscountCoupon * -1;
}
}
//代理店手数料率でソートされた商品の配列をループ
foreach ($sortedOrderItems as $OrderItem) {
// 商品個数分だけループ
for ($i = 0; $i < $OrderItem->getQuantity(); $i++) {
//商品名の取得
$OrderItemName = $OrderItem->getProductName();
error_log($OrderItemName . 'の代理店手数料計算処理を開始');
//商品情報の取得
$ProductClass = $OrderItem->getProductClass();
// 商品IDの取得
$ProductId = $OrderItem->getProductClass()->getProduct()->getId();
error_log($ProductId . ' 商品ID (Type: ' . gettype($ProductId) . ')');
// 割引詳細から該当商品の割引額を取得
$regularDiscountAmount = array_key_exists($ProductId, $discountDetails) ? $discountDetails[$ProductId] : 0;
error_log('定期割引取得結果' . $regularDiscountAmount);
error_log('ID' . $ProductId);
// discountDetailsの中身をエラーログに出力
error_log('Discount Details: ' . print_r($discountDetails, true));
//商品以外の場合スキップ
if (!$ProductClass || $OrderItem->getPrice() < 0) {
error_log($OrderItemName . 'は商品以外なのでスキップ');
continue;
}
// error_log('定期商品の値引き合計値: ' . $regularDiscountAmount);
//商品の金額(税込)
$OrderItemPrice = $OrderItem->getPrice();
// 丸め規則はデフォルトの課税規則に従う
$OrderItemPrice = $OrderItemPrice + $this->taxRuleService->calcTax(
$OrderItemPrice,
$taxRate,
$taxRoundingType,
$taxAdjust
);
// error_log($OrderItemPrice . 'が商品の金額');
//商品に設定された代理店手数料率(%)を取得
$agencyCommissionRate = $ProductClass->getAgencyCommission();
// error_log($agencyCommissionRate . 'が代理店手数料率');
//商品の税込サブトータル = 商品金額
$subtotalWithTax = $OrderItemPrice;
// error_log($subtotal . '商品の税抜サブトータル');
//商品の税込サブトータル
// error_log($subtotalWithTax . '商品の税込サブトータル');
//クーポンがある場合
if ($flgDiscountCoupon || $flgPostageCoupon || $flgRebateCoupon) {
// error_log('値引きクーポンの処理');
//クーポン残価が正の場合
if ($amountDiscountCouponBalance > 0) {
//クーポン残価がサブトータルより高い場合
if ($amountDiscountCouponBalance > $subtotalWithTax) {
//代理店手数料を反映させる金額は0
$subtotalWithTaxAndDiscount = 0;
}
//クーポン残価がサブトータルより低い場合
else {
// 代理店手数料を反映させる金額(サブトータル(税込) - 値引クーポンの残価 - 定期受注割引)を計算
$subtotalWithTaxAndDiscount = $subtotalWithTax - $amountDiscountCouponBalance - $regularDiscountAmount;
//理店手数料を反映させる金額が負の場合は0
if ($subtotalWithTaxAndDiscount < 0) {
$subtotalWithTaxAndDiscount = 0;
}
}
//クーポン残価の更新
$amountDiscountCouponBalance = $amountDiscountCouponBalance - $subtotalWithTax + $regularDiscountAmount;
// error_log($subtotalWithTax . '値引きクーポン対象の商品');
// error_log('値引きクーポンの処理');
// error_log('代理店手数料反映金額:'.$subtotalWithTaxAndDiscount);
// error_log('代理店手数料反映金額計算:'.$subtotalWithTax.'-'.$amountDiscountCoupon.'-'.$regularDiscountAmount);
} else {
// error_log('値引きクーポン対象外の商品');
// 代理店手数料を反映させる金額(サブトータル(税込) - 定期受注割引)を計算
$subtotalWithTaxAndDiscount = $subtotalWithTax - $regularDiscountAmount;
// error_log('代理店手数料反映金額:'.$subtotalWithTaxAndDiscount);
}
}
//クーポンがない場合
else {
// 代理店手数料を反映させる金額(サブトータル(税込)- 定期受注割引)を計算
$subtotalWithTaxAndDiscount = $subtotalWithTax - $regularDiscountAmount;
}
error_log('代理店手数料反映金額:' . $subtotalWithTaxAndDiscount);
error_log('商品金額(端数処理前):' . $OrderItem->getPrice());
error_log('商品金額:' . $OrderItemPrice);
error_log('小計(税込):' . $subtotalWithTax);
error_log('純粋な手数料金額:' . $subtotalWithTax * ($agencyCommissionRate / 100.0));
error_log('割引金額:' . $regularDiscountAmount);
//代理店手数料を計算
if ($agencyCommissionRate > 0) {
$commissionAmount = $subtotalWithTaxAndDiscount * ($agencyCommissionRate / 100.0);
} else {
$commissionAmount = 0;
}
// error_log('代理店手数料:'.$commissionAmount);
// 代理店手数料に端数処理を適用(切り捨て)
error_log('手数料結果端数処理前:' . $commissionAmount);
$commissionAmountRounded = $this->applyRounding($commissionAmount, 2);
error_log('手数料結果:' . $commissionAmountRounded);
// error_log('端数処理した代理店手数料:'.$commissionAmountRounded);
// 代理店手数料の合計に加算
$totalAgencyCommission += $commissionAmountRounded;
// 各商品の個数を合計
$totalProductQuantity += $OrderItem->getQuantity();
}
}
// 受注に代理店手数料を設定
// 登録処理OK
$Order->setAgencyCommissionAmount($totalAgencyCommission);
// error_log('代理店手数料合計金額:'.$commissionAmountRounded);
// 商品の総個数を設定
$Order->setProductQuantity($totalProductQuantity);
// agency_codeがnullではない場合、その値をagency_order_codeに設定
$Order->setAgencyOrderCode($Customer->getAgencyCode());
} else {
// 会員でない、またはagency_codeがnullの場合、代理店手数料を0に設定
$totalAgencyCommission = 0;
$Order->setAgencyCommissionAmount($totalAgencyCommission);
}
}
//========================
//税計算
//1:四捨五入
//2:切り捨て
//3:切り上げ
//========================
protected function applyRounding($amount, $roundingType)
{
switch ($roundingType) {
case 1: // 四捨五入
return (int)round($amount);
case 2: // 切り捨て
return (int)floor($amount);
case 3: // 切り上げ
return (int)ceil($amount);
default:
// デフォルト処理
return (int)round($amount);
}
}
}