| @@ -74,6 +74,7 @@ class FillPaymentPlan extends BaseCommand | |||||
| // プランの取得 | // プランの取得 | ||||
| $plans = $this->getPlans(); | $plans = $this->getPlans(); | ||||
| foreach ($plans as $plan) { | foreach ($plans as $plan) { | ||||
| // 契約の取得 | // 契約の取得 | ||||
| @@ -0,0 +1,129 @@ | |||||
| <?php | |||||
| namespace App\Console\Commands\PoolTransfer; | |||||
| use App\Console\Commands\BaseCommand; | |||||
| use App\Email\Members\EntryPaymentComplete; | |||||
| use App\Exceptions\SkipException; | |||||
| use App\Kintone\KintoneRecordQueryOperator; | |||||
| use App\Kintone\Models\PaymentPlan; | |||||
| use App\Kintone\Models\Pool; | |||||
| use App\Kintone\Models\SeasonTicketContractEntry; | |||||
| use App\Logic\EmailManager; | |||||
| use App\Logic\PoolAttachManager; | |||||
| class AttachToPaymentPlan extends BaseCommand | |||||
| { | |||||
| const COMMAND = "pool-transfer:attach-to-payment-plan"; | |||||
| /** | |||||
| * The name and signature of the console command. | |||||
| * | |||||
| * @var string | |||||
| */ | |||||
| protected $signature = self::COMMAND; | |||||
| /** | |||||
| * The console command description. | |||||
| * | |||||
| * @var string | |||||
| */ | |||||
| protected $description = '入金予定実績へ充当する'; | |||||
| static public function getCommand() | |||||
| { | |||||
| return self::COMMAND; | |||||
| } | |||||
| /** | |||||
| * Create a new command instance. | |||||
| * | |||||
| * @return void | |||||
| */ | |||||
| public function __construct() | |||||
| { | |||||
| parent::__construct(); | |||||
| } | |||||
| /** | |||||
| * Execute the console command. | |||||
| * | |||||
| * @return int | |||||
| */ | |||||
| public function service(): int | |||||
| { | |||||
| $targets = $this->getTargets(); | |||||
| $this->outputInfo(sprintf("総件数:%d件", $targets->count())); | |||||
| foreach ($targets as $index => $target) { | |||||
| // 100件ごとに経過を出力 | |||||
| if ($index % 100 === 0) { | |||||
| $this->outputInfo(sprintf("処理中:%d件目", $index + 1)); | |||||
| } | |||||
| try { | |||||
| $this->handleData($target); | |||||
| } catch (SkipException $e) { | |||||
| $this->outputWarn($e->getMessage()); | |||||
| } | |||||
| } | |||||
| return self::RESULTCODE_SUCCESS; | |||||
| } | |||||
| public function handleData(Pool $pool) | |||||
| { | |||||
| $manager = new PoolAttachManager(); | |||||
| $manager->attach($pool) | |||||
| ->save(); | |||||
| // 初回振り込み完了処理 | |||||
| if ($manager->getFirstPaymnetEntryRecordIds()->isNotEmpty()) { | |||||
| foreach ($manager->getFirstPaymnetEntryRecordIds() as $entryRecordId) { | |||||
| $this->handleFirstPaymentDone($entryRecordId); | |||||
| } | |||||
| } | |||||
| } | |||||
| public function handleFirstPaymentDone(int $entryRecordNo) | |||||
| { | |||||
| // 未払いチェック | |||||
| $query = PaymentPlan::getQuery()->where(PaymentPlan::FIELD_FIRST_PAYMENT_ENTRY_RECORD_NO, $entryRecordNo); | |||||
| $plans = PaymentPlan::getAccess()->all($query); | |||||
| if ($plans->isEmpty()) { | |||||
| return; | |||||
| } | |||||
| // 未払い検索 | |||||
| $未払い = $plans->first((function (PaymentPlan $plan) { | |||||
| return !$plan->appropriationDate || $plan->appropriationAmount !== $plan->paymentPlanAmount; | |||||
| })); | |||||
| if ($未払い) { | |||||
| return; | |||||
| } | |||||
| // 承認メール送信 | |||||
| $entry = SeasonTicketContractEntry::find($entryRecordNo); | |||||
| $parking = $entry->getParking(); | |||||
| $plan = $entry->getPlan(); | |||||
| $email = new EntryPaymentComplete($parking, $entry, $plan); | |||||
| (new EmailManager($email))->confirm(); | |||||
| } | |||||
| public function getTargets() | |||||
| { | |||||
| $query = Pool::getQuery()->where(Pool::FIELD_POOL_AMOUNT, 0, KintoneRecordQueryOperator::GT); | |||||
| $ret = Pool::getAccess()->all($query); | |||||
| return $ret; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,124 @@ | |||||
| <?php | |||||
| namespace App\Console\Commands\PoolTransfer; | |||||
| use App\Console\Commands\BaseCommand; | |||||
| use App\Exceptions\SkipException; | |||||
| use App\Kintone\KintoneRecordQueryOperator; | |||||
| use App\Kintone\Models\DropDown\SmbcPayment\SmbcPaymentStatus; | |||||
| use App\Kintone\Models\SmbcAccountTransferResult; | |||||
| use App\Kintone\Models\SmbcPayment; | |||||
| use App\Kintone\Models\YuchoPaymentResult; | |||||
| use App\Logic\PoolTransferManager; | |||||
| use App\Util\CollectionUtil; | |||||
| use Illuminate\Support\Collection; | |||||
| class MoveToPool extends BaseCommand | |||||
| { | |||||
| const COMMAND = "pool-transfer:move-to-pool"; | |||||
| /** | |||||
| * The name and signature of the console command. | |||||
| * | |||||
| * @var string | |||||
| */ | |||||
| protected $signature = self::COMMAND; | |||||
| /** | |||||
| * The console command description. | |||||
| * | |||||
| * @var string | |||||
| */ | |||||
| protected $description = '入金プールへ異動する'; | |||||
| static public function getCommand() | |||||
| { | |||||
| return self::COMMAND; | |||||
| } | |||||
| /** | |||||
| * Create a new command instance. | |||||
| * | |||||
| * @return void | |||||
| */ | |||||
| public function __construct() | |||||
| { | |||||
| parent::__construct(); | |||||
| } | |||||
| /** | |||||
| * Execute the console command. | |||||
| * | |||||
| * @return int | |||||
| */ | |||||
| public function service(): int | |||||
| { | |||||
| $targets = $this->getTargets(); | |||||
| $this->outputInfo(sprintf("総件数:%d件", $targets->count())); | |||||
| foreach ($targets as $target) { | |||||
| try { | |||||
| $this->handleData($target); | |||||
| } catch (SkipException $e) { | |||||
| $this->outputWarn($e->getMessage()); | |||||
| } | |||||
| } | |||||
| return self::RESULTCODE_SUCCESS; | |||||
| } | |||||
| public function handleData(YuchoPaymentResult|SmbcPayment|SmbcAccountTransferResult $payment) | |||||
| { | |||||
| $manager = new PoolTransferManager(); | |||||
| $manager->moveToPool($payment); | |||||
| } | |||||
| /** | |||||
| * @return Collection<int ,YuchoPaymentResult|SmbcPayment|SmbcAccountTransferResult> | |||||
| */ | |||||
| public function getTargets() | |||||
| { | |||||
| /** | |||||
| * @var Collection<int ,YuchoPaymentResult|SmbcPayment|SmbcAccountTransferResult> | |||||
| */ | |||||
| $ret = collect(); | |||||
| // ゆうちょ振込 | |||||
| $query = YuchoPaymentResult::getQuery()->whereNotIn(YuchoPaymentResult::FIELD_POOL_DONE, ["済"]) | |||||
| ->whereNotNull(YuchoPaymentResult::FIELD_CUSTOMER_CODE) | |||||
| ->where(YuchoPaymentResult::FIELD_PAYMENT_AMOUNT, 0, KintoneRecordQueryOperator::GT); | |||||
| $targets = YuchoPaymentResult::getAccess()->all($query); | |||||
| $ret->concat($targets); | |||||
| CollectionUtil::pushAll($ret, $targets); | |||||
| $this->outputInfo(sprintf("ゆうちょ件数:%d件", $targets->count())); | |||||
| // コンビニ支払 | |||||
| $query = SmbcPayment::getQuery()->whereNotIn(SmbcPayment::FIELD_POOL_DONE, ["済"]) | |||||
| ->whereNotNull(SmbcPayment::FIELD_CUSTOMER_CODE) | |||||
| ->where(SmbcPayment::FIELD_PAYMENT_AMOUNT, 0, KintoneRecordQueryOperator::GT) | |||||
| ->whereIn(SmbcPayment::FIELD_STATUS, [SmbcPaymentStatus::S002_決済結果待ち]); | |||||
| $targets = SmbcPayment::getAccess()->all($query); | |||||
| CollectionUtil::pushAll($ret, $targets); | |||||
| $this->outputInfo(sprintf("コンビニ支払:%d件", $targets->count())); | |||||
| // 口座振替 | |||||
| $query = SmbcAccountTransferResult::getQuery()->whereNotIn(SmbcAccountTransferResult::FIELD_POOL_DONE, ["済"]) | |||||
| ->whereNotNull(SmbcAccountTransferResult::FIELD_CUSTOMER_CODE) | |||||
| ->where(SmbcAccountTransferResult::FIELD_PAYMENT_AMOUNT, 0, KintoneRecordQueryOperator::GT); | |||||
| $targets = SmbcAccountTransferResult::getAccess()->all($query); | |||||
| CollectionUtil::pushAll($ret, $targets); | |||||
| $this->outputInfo(sprintf("口座振替:%d件", $targets->count())); | |||||
| return $ret; | |||||
| } | |||||
| } | |||||
| @@ -18,6 +18,7 @@ class Kernel extends ConsoleKernel | |||||
| Schedules\BankAccountRegisterRemaind::register($schedule); | Schedules\BankAccountRegisterRemaind::register($schedule); | ||||
| Schedules\SeasonTikcetContractSelectionSetResult::register($schedule); | Schedules\SeasonTikcetContractSelectionSetResult::register($schedule); | ||||
| Schedules\SeasonTicketContractTerminateComplete::register($schedule); | Schedules\SeasonTicketContractTerminateComplete::register($schedule); | ||||
| Schedules\PoolTransfer::register($schedule); | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -0,0 +1,22 @@ | |||||
| <?php | |||||
| namespace App\Console\Schedules; | |||||
| use App\Console\Commands\PoolTransfer\MoveToPool; | |||||
| use App\Console\Commands\PoolTransfer\AttachToPaymentPlan; | |||||
| use Illuminate\Console\Scheduling\Schedule; | |||||
| use Illuminate\Support\Facades\Artisan; | |||||
| class PoolTransfer extends BaseSchedule | |||||
| { | |||||
| static public function register(Schedule $schedule) | |||||
| { | |||||
| $schedule->command(MoveToPool::class) | |||||
| ->twiceDaily(10, 16) | |||||
| ->description("プール金異動") | |||||
| ->onSuccess(function () { | |||||
| Artisan::call(AttachToPaymentPlan::class); | |||||
| }); | |||||
| } | |||||
| } | |||||
| @@ -11,7 +11,7 @@ class SMBCBankAccountRegisterPoll extends BaseSchedule | |||||
| static public function register(Schedule $schedule) | static public function register(Schedule $schedule) | ||||
| { | { | ||||
| $schedule->command(Command::class) | $schedule->command(Command::class) | ||||
| ->everyThreeMinutes() | |||||
| ->everyOddHour() | |||||
| ->unlessBetween('1:00', '6:00') | ->unlessBetween('1:00', '6:00') | ||||
| ->description("SMBC口座振替申請結果取得"); | ->description("SMBC口座振替申請結果取得"); | ||||
| } | } | ||||
| @@ -11,8 +11,8 @@ class SMBCPaymentPoll extends BaseSchedule | |||||
| static public function register(Schedule $schedule) | static public function register(Schedule $schedule) | ||||
| { | { | ||||
| $schedule->command(Command::class) | $schedule->command(Command::class) | ||||
| ->everyTwoHours() | |||||
| ->twiceDailyAt(9, 15, 30) | |||||
| ->unlessBetween('1:00', '6:00') | ->unlessBetween('1:00', '6:00') | ||||
| ->description("SMBC支払結果取得"); | |||||
| ->description("SMBC支払結果取得(コンビニ)"); | |||||
| } | } | ||||
| } | } | ||||
| @@ -43,7 +43,6 @@ class EntryPaymentComplete extends Members | |||||
| 'use_start_date' => $entry->useStartDate ? $entry->useStartDate->format('Y/m/d') : "-", | 'use_start_date' => $entry->useStartDate ? $entry->useStartDate->format('Y/m/d') : "-", | ||||
| 'payment_method' => $entry->paymentMethod ?? "-", | 'payment_method' => $entry->paymentMethod ?? "-", | ||||
| 'amount' => number_format($entry->amount ?? 0), | 'amount' => number_format($entry->amount ?? 0), | ||||
| 'paymentExplain' => EntryMessageBuilder::getPaymentExplainStr($this->entry), | |||||
| 'taxExplain' => EntryMessageBuilder::getTaxExplain($this->plan), | 'taxExplain' => EntryMessageBuilder::getTaxExplain($this->plan), | ||||
| 'can_terminate_15' => in_array(Parking::ELEMENT_CAN_TERMINATE_DATE_15, $this->parking->canTerminateDate), | 'can_terminate_15' => in_array(Parking::ELEMENT_CAN_TERMINATE_DATE_15, $this->parking->canTerminateDate), | ||||
| 'can_terminate_end_of_month' => in_array(Parking::ELEMENT_CAN_TERMINATE_DATE_END_OF_MONTH, $this->parking->canTerminateDate), | 'can_terminate_end_of_month' => in_array(Parking::ELEMENT_CAN_TERMINATE_DATE_END_OF_MONTH, $this->parking->canTerminateDate), | ||||
| @@ -0,0 +1,9 @@ | |||||
| <?php | |||||
| namespace App\Exceptions; | |||||
| use Exception; | |||||
| class SkipException extends Exception | |||||
| { | |||||
| } | |||||
| @@ -0,0 +1,10 @@ | |||||
| <?php | |||||
| namespace App\Kintone\Models\DropDown\PoolTransferHistory; | |||||
| abstract class TransferMethod | |||||
| { | |||||
| const ACCOUNT_TRANSFER = "口座振替"; | |||||
| const YUCHO = "ゆうちょ振込"; | |||||
| const CVS = "コンビニ支払"; | |||||
| } | |||||
| @@ -0,0 +1,9 @@ | |||||
| <?php | |||||
| namespace App\Kintone\Models\DropDown\PoolTransferHistory; | |||||
| abstract class TransgerType | |||||
| { | |||||
| const INCOME = "入金"; | |||||
| const ATTACH = "充当"; | |||||
| } | |||||
| @@ -453,6 +453,10 @@ abstract class KintoneModel | |||||
| public function save() | public function save() | ||||
| { | { | ||||
| if (count($this->changed) === 0) { | |||||
| return; | |||||
| } | |||||
| $access = static::getAccess(); | $access = static::getAccess(); | ||||
| if ($this->recordId === null) { | if ($this->recordId === null) { | ||||
| $access->create($this); | $access->create($this); | ||||
| @@ -23,7 +23,9 @@ use Illuminate\Support\Collection; | |||||
| * @property Carbon appropriationDate | * @property Carbon appropriationDate | ||||
| * @property int appropriationAmount | * @property int appropriationAmount | ||||
| * @property int remainingAmount | * @property int remainingAmount | ||||
| * @property int firstPaymentEntryRecordNo | |||||
| * @property Collection<int ,Deposit> depositList | * @property Collection<int ,Deposit> depositList | ||||
| * @property string[] partialFee | |||||
| */ | */ | ||||
| class PaymentPlan extends KintoneModel | class PaymentPlan extends KintoneModel | ||||
| { | { | ||||
| @@ -42,9 +44,11 @@ class PaymentPlan extends KintoneModel | |||||
| const FIELD_APPROPRIATION_DATE = "appropriation_date"; | const FIELD_APPROPRIATION_DATE = "appropriation_date"; | ||||
| const FIELD_APPROPRIATION_AMOUNT = "appropriation_amount"; | const FIELD_APPROPRIATION_AMOUNT = "appropriation_amount"; | ||||
| const FIELD_REMAINING_AMOUNT = "remaining_amount"; | const FIELD_REMAINING_AMOUNT = "remaining_amount"; | ||||
| const FIELD_FIRST_PAYMENT_ENTRY_RECORD_NO = "first_payment_entry_record_no"; | |||||
| const FIELD_DEPOSIT_LIST = "保証金明細"; | const FIELD_DEPOSIT_LIST = "保証金明細"; | ||||
| const FIELD_DEPOSIT_LIST_NAME = "保証金明細_名称"; | const FIELD_DEPOSIT_LIST_NAME = "保証金明細_名称"; | ||||
| const FIELD_DEPOSIT_LIST_AMOUNT = "保証金明細_金額"; | const FIELD_DEPOSIT_LIST_AMOUNT = "保証金明細_金額"; | ||||
| const FIELD_PARTIAL_FEE = "定期料金日割り分"; | |||||
| protected const FIELDS = [ | protected const FIELDS = [ | ||||
| ...parent::FIELDS, | ...parent::FIELDS, | ||||
| @@ -61,7 +65,9 @@ class PaymentPlan extends KintoneModel | |||||
| self::FIELD_APPROPRIATION_DATE => FieldType::DATE, | self::FIELD_APPROPRIATION_DATE => FieldType::DATE, | ||||
| self::FIELD_APPROPRIATION_AMOUNT => FieldType::NUMBER, | self::FIELD_APPROPRIATION_AMOUNT => FieldType::NUMBER, | ||||
| self::FIELD_REMAINING_AMOUNT => FieldType::NUMBER, | self::FIELD_REMAINING_AMOUNT => FieldType::NUMBER, | ||||
| self::FIELD_FIRST_PAYMENT_ENTRY_RECORD_NO => FieldType::NUMBER, | |||||
| self::FIELD_DEPOSIT_LIST => FieldType::SUBTABLE, | self::FIELD_DEPOSIT_LIST => FieldType::SUBTABLE, | ||||
| self::FIELD_PARTIAL_FEE => FieldType::CHECK_BOX, | |||||
| ]; | ]; | ||||
| protected const SUB_TABLES = [ | protected const SUB_TABLES = [ | ||||
| @@ -87,14 +93,37 @@ class PaymentPlan extends KintoneModel | |||||
| ]; | ]; | ||||
| } | } | ||||
| /** | |||||
| * 支払い済みか判定 | |||||
| * | |||||
| * @return boolean | |||||
| */ | |||||
| public function donePayment(): bool | public function donePayment(): bool | ||||
| { | { | ||||
| $paymentDate = $this->getDate(self::FIELD_APPROPRIATION_DATE); | $paymentDate = $this->getDate(self::FIELD_APPROPRIATION_DATE); | ||||
| $remainingAmount = $this->getNumber(self::FIELD_REMAINING_AMOUNT); | $remainingAmount = $this->getNumber(self::FIELD_REMAINING_AMOUNT); | ||||
| $paymentPlanAmount = $this->getNumber(self::FIELD_PAYMENT_PLAN_AMOUNT); | |||||
| $paymentAmount = $this->getNumber(self::FIELD_APPROPRIATION_AMOUNT); | |||||
| return !!$paymentDate && $remainingAmount === 0; | |||||
| return !!$paymentDate && $remainingAmount === 0 && $paymentPlanAmount === $paymentAmount; | |||||
| } | } | ||||
| /** | |||||
| * 日割り分の請求データか判定 | |||||
| * | |||||
| * @return boolean | |||||
| */ | |||||
| public function isPartialFee(): bool | |||||
| { | |||||
| $target = $this->partialFee; | |||||
| if (is_array($target) && !!count($target)) { | |||||
| return true; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| private function getViewName(): string | private function getViewName(): string | ||||
| { | { | ||||
| @@ -0,0 +1,36 @@ | |||||
| <?php | |||||
| namespace App\Kintone\Models; | |||||
| use Illuminate\Support\Carbon; | |||||
| /** | |||||
| * アプリ名 入金プール | |||||
| * | |||||
| * @property int customerCode | |||||
| * @property string customerName | |||||
| * @property int poolAmount | |||||
| */ | |||||
| class Pool extends KintoneModel | |||||
| { | |||||
| const CONFIG_KEY = "KINTONE_APP_POOL"; | |||||
| const FIELD_CUSTOMER_CODE = "顧客コード"; | |||||
| const FIELD_CUSTOMER_NAME = "顧客名"; | |||||
| const FIELD_POOL_AMOUNT = "プール金"; | |||||
| protected const FIELDS = [ | |||||
| ...parent::FIELDS, | |||||
| self::FIELD_CUSTOMER_CODE => FieldType::NUMBER, | |||||
| self::FIELD_CUSTOMER_NAME => FieldType::SINGLE_LINE_TEXT, | |||||
| self::FIELD_POOL_AMOUNT => FieldType::NUMBER, | |||||
| ]; | |||||
| protected const FIELD_NAMES = [ | |||||
| ...parent::FIELD_NAMES, | |||||
| ]; | |||||
| protected const RELATIONS = [ | |||||
| Customer::class, | |||||
| ]; | |||||
| } | |||||
| @@ -0,0 +1,75 @@ | |||||
| <?php | |||||
| namespace App\Kintone\Models; | |||||
| use Illuminate\Support\Carbon; | |||||
| /** | |||||
| * アプリ名 プール金・異動履歴 | |||||
| * | |||||
| * @property int poolRecordNo | |||||
| * @property int customerCode | |||||
| * @property string customerName | |||||
| * @property Carbon transferDatetime | |||||
| * @property string transferType | |||||
| * @property int transferAmount | |||||
| * @property int poolAmountBefore | |||||
| * @property int poolAmountAfter | |||||
| * @property ?string incomeMethod | |||||
| * @property ?int incomeAccountTransferResultRecordNo | |||||
| * @property ?int incomeYuchoTransferRecordNo | |||||
| * @property ?int incomeCvsPaymentRecordNo | |||||
| * @property ?int paymentPlanRecordNo | |||||
| * @property ?int seasonContractRecordNo | |||||
| */ | |||||
| class PoolTransferHistory extends KintoneModel | |||||
| { | |||||
| const CONFIG_KEY = "KINTONE_APP_POOL_TRANSFER_HISTORY"; | |||||
| const FIELD_POOL_RECORD_NO = "入金プールレコード番号"; | |||||
| const FIELD_CUSTOMER_CODE = "顧客コード"; | |||||
| const FIELD_CUSTOMER_NAME = "顧客名"; | |||||
| const FIELD_TRANSFER_DATETIME = "異動日時"; | |||||
| const FIELD_TRANSFER_TYPE = "種別"; | |||||
| const FIELD_TRANSFER_AMOUNT = "異動金額"; | |||||
| const FIELD_POOL_AMOUNT_BEFORE = "異動前残高"; | |||||
| const FIELD_POOL_AMOUNT_AFTER = "異動後残高"; | |||||
| const FIELD_INCOME_METHOD = "入金手段"; | |||||
| const FIELD_INCOME_ACCOUNT_TRANSFER_RESULT_RECORD_NO = "口座振替結果レコード番号"; | |||||
| const FIELD_INCOME_YUCHO_TRANSFER_RECORD_NO = "ゆうちょ振込レコード番号"; | |||||
| const FIELD_INCOME_CVS_PAYMENT_RECORD_NO = "コンビニ支払レコード番号"; | |||||
| const FIELD_PAYMENT_PLAN_RECORD_NO = "入金予定結果レコード番号"; | |||||
| const FIELD_SEASON_CONTRACT_RECORD_NO = "車室情報管理レコード番号"; | |||||
| protected const FIELDS = [ | |||||
| ...parent::FIELDS, | |||||
| self::FIELD_POOL_RECORD_NO => FieldType::NUMBER, | |||||
| self::FIELD_CUSTOMER_CODE => FieldType::NUMBER, | |||||
| self::FIELD_CUSTOMER_NAME => FieldType::SINGLE_LINE_TEXT, | |||||
| self::FIELD_TRANSFER_DATETIME => FieldType::DATETIME, | |||||
| self::FIELD_TRANSFER_TYPE => FieldType::DROP_DOWN, | |||||
| self::FIELD_TRANSFER_AMOUNT => FieldType::NUMBER, | |||||
| self::FIELD_POOL_AMOUNT_BEFORE => FieldType::NUMBER, | |||||
| self::FIELD_POOL_AMOUNT_AFTER => FieldType::NUMBER, | |||||
| self::FIELD_INCOME_METHOD => FieldType::SINGLE_LINE_TEXT, | |||||
| self::FIELD_INCOME_ACCOUNT_TRANSFER_RESULT_RECORD_NO => FieldType::NUMBER, | |||||
| self::FIELD_INCOME_YUCHO_TRANSFER_RECORD_NO => FieldType::NUMBER, | |||||
| self::FIELD_INCOME_CVS_PAYMENT_RECORD_NO => FieldType::NUMBER, | |||||
| self::FIELD_PAYMENT_PLAN_RECORD_NO => FieldType::NUMBER, | |||||
| self::FIELD_SEASON_CONTRACT_RECORD_NO => FieldType::NUMBER, | |||||
| ]; | |||||
| protected const FIELD_NAMES = [ | |||||
| ...parent::FIELD_NAMES, | |||||
| ]; | |||||
| protected const RELATIONS = [ | |||||
| Customer::class, | |||||
| YuchoPaymentResult::class, | |||||
| SmbcPayment::class, | |||||
| SmbcAccountTransferResult::class, | |||||
| Pool::class, | |||||
| PaymentPlan::class, | |||||
| ]; | |||||
| } | |||||
| @@ -0,0 +1,40 @@ | |||||
| <?php | |||||
| namespace App\Kintone\Models; | |||||
| use Illuminate\Support\Carbon; | |||||
| /** | |||||
| * アプリ名 SMBC口座振替結果 | |||||
| * | |||||
| * @property int customerCode | |||||
| * @property string customerName | |||||
| * @property Carbon paymentDate | |||||
| * @property int paymentAmount | |||||
| * @property string[] poolDone | |||||
| */ | |||||
| class SmbcAccountTransferResult extends KintoneModel | |||||
| { | |||||
| const CONFIG_KEY = "KINTONE_APP_SMBC_ACCOUNT_TRANSFER_RESULT"; | |||||
| const FIELD_CUSTOMER_CODE = "顧客コード"; | |||||
| const FIELD_CUSTOMER_NAME = "顧客名"; | |||||
| const FIELD_PAYMENT_DATE = "引落日"; | |||||
| const FIELD_PAYMENT_AMOUNT = "引落金額"; | |||||
| const FIELD_POOL_DONE = "プール済み"; | |||||
| const FIELD_RESULT_CODE = "結果コード"; | |||||
| protected const FIELDS = [ | |||||
| ...parent::FIELDS, | |||||
| self::FIELD_CUSTOMER_CODE => FieldType::NUMBER, | |||||
| self::FIELD_CUSTOMER_NAME => FieldType::SINGLE_LINE_TEXT, | |||||
| self::FIELD_PAYMENT_DATE => FieldType::DATE, | |||||
| self::FIELD_PAYMENT_AMOUNT => FieldType::NUMBER, | |||||
| self::FIELD_POOL_DONE => FieldType::CHECK_BOX, | |||||
| self::FIELD_RESULT_CODE => FieldType::SINGLE_LINE_TEXT, | |||||
| ]; | |||||
| protected const FIELD_NAMES = [ | |||||
| ...parent::FIELD_NAMES, | |||||
| ]; | |||||
| } | |||||
| @@ -12,6 +12,7 @@ use Illuminate\Support\Carbon; | |||||
| * @property string orderNo | * @property string orderNo | ||||
| * @property int customerCode | * @property int customerCode | ||||
| * @property int claimAmount | * @property int claimAmount | ||||
| * @property string[] poolDone | |||||
| * @property string acceptNo | * @property string acceptNo | ||||
| * @property string paymentStatus | * @property string paymentStatus | ||||
| * @property ?Carbon paymentStatusUpdateDatetime | * @property ?Carbon paymentStatusUpdateDatetime | ||||
| @@ -30,6 +31,8 @@ class SmbcPayment extends KintoneModel | |||||
| const FIELD_ORDER_NO = "請求_補助_番号"; | const FIELD_ORDER_NO = "請求_補助_番号"; | ||||
| const FIELD_CUSTOMER_CODE = "顧客コード"; | const FIELD_CUSTOMER_CODE = "顧客コード"; | ||||
| const FIELD_CLAIM_AMOUNT = "請求金額"; | const FIELD_CLAIM_AMOUNT = "請求金額"; | ||||
| const FIELD_POOL_DONE = "プール済み"; | |||||
| const FIELD_ACCEPT_NO = "決済受付番号"; | const FIELD_ACCEPT_NO = "決済受付番号"; | ||||
| const FIELD_PAYMENT_STATUS = "決済ステータス"; | const FIELD_PAYMENT_STATUS = "決済ステータス"; | ||||
| @@ -0,0 +1,38 @@ | |||||
| <?php | |||||
| namespace App\Kintone\Models; | |||||
| use Illuminate\Support\Carbon; | |||||
| /** | |||||
| * アプリ名 ゆうちょ振込情報 | |||||
| * | |||||
| * @property int customerCode | |||||
| * @property string customerName | |||||
| * @property Carbon paymentDate | |||||
| * @property int paymentAmount | |||||
| * @property string[] poolDone | |||||
| */ | |||||
| class YuchoPaymentResult extends KintoneModel | |||||
| { | |||||
| const CONFIG_KEY = "KINTONE_APP_YUCHO_PAYMENT_RESULT"; | |||||
| const FIELD_CUSTOMER_CODE = "顧客コード"; | |||||
| const FIELD_CUSTOMER_NAME = "顧客名"; | |||||
| const FIELD_PAYMENT_DATE = "取引日"; | |||||
| const FIELD_PAYMENT_AMOUNT = "受入金額"; | |||||
| const FIELD_POOL_DONE = "プール済み"; | |||||
| protected const FIELDS = [ | |||||
| ...parent::FIELDS, | |||||
| self::FIELD_CUSTOMER_CODE => FieldType::NUMBER, | |||||
| self::FIELD_CUSTOMER_NAME => FieldType::SINGLE_LINE_TEXT, | |||||
| self::FIELD_PAYMENT_DATE => FieldType::DATE, | |||||
| self::FIELD_PAYMENT_AMOUNT => FieldType::NUMBER, | |||||
| self::FIELD_POOL_DONE => FieldType::CHECK_BOX, | |||||
| ]; | |||||
| protected const FIELD_NAMES = [ | |||||
| ...parent::FIELD_NAMES, | |||||
| ]; | |||||
| } | |||||
| @@ -0,0 +1,192 @@ | |||||
| <?php | |||||
| namespace App\Logic; | |||||
| use App\Exceptions\SkipException; | |||||
| use App\Kintone\KintoneRecordQuery; | |||||
| use App\Kintone\Models\DropDown\PaymentPlan\PaymentType; | |||||
| use App\Kintone\Models\DropDown\PoolTransferHistory\TransgerType; | |||||
| use App\Kintone\Models\PaymentPlan; | |||||
| use App\Kintone\Models\Pool; | |||||
| use App\Kintone\Models\PoolTransferHistory; | |||||
| use App\Util\DateUtil; | |||||
| use Exception; | |||||
| use Illuminate\Support\Collection; | |||||
| /** | |||||
| * 入金プールから入金予定結果へ異動する | |||||
| */ | |||||
| class PoolAttachManager | |||||
| { | |||||
| private Pool|null $pool = null; | |||||
| /** | |||||
| * @var Collection<int, PoolTransferHistory> | |||||
| */ | |||||
| private Collection $histories; | |||||
| /** | |||||
| * @var Collection<int, PaymentPlan> | |||||
| */ | |||||
| private Collection $plans; | |||||
| /** | |||||
| * @var Collection<int, PaymentPlan> | |||||
| */ | |||||
| private Collection $completePlans; | |||||
| /** | |||||
| * @var Collection<int, bool> | |||||
| */ | |||||
| private Collection $firstPaymnetEntryRecordIds; | |||||
| public function attach(Pool $pool) | |||||
| { | |||||
| $this->clear(); | |||||
| if ($pool->poolAmount === 0) { | |||||
| throw new SkipException(sprintf("プール金なし 顧客コード:%d", $pool->customerCode)); | |||||
| } | |||||
| $this->pool = $pool; | |||||
| $this->plans = $this->getPaymentPlans(); | |||||
| foreach ($this->plans as $plan) { | |||||
| try { | |||||
| if (!$this->canAttach($plan)) { | |||||
| throw new AttachStop(sprintf("残高不足のため充当途中停止 顧客コード:%d", $this->pool->customerCode)); | |||||
| } | |||||
| $this->makeHistory($plan); | |||||
| $this->setPoolAmount($plan); | |||||
| $this->setPaymentPlanDone($plan); | |||||
| } catch (AttachStop $e) { | |||||
| logs()->warning($e->getMessage()); | |||||
| break; | |||||
| } | |||||
| } | |||||
| return $this; | |||||
| } | |||||
| public function getFirstPaymnetEntryRecordIds() | |||||
| { | |||||
| return $this->firstPaymnetEntryRecordIds->keys(); | |||||
| } | |||||
| private function getPaymentPlans() | |||||
| { | |||||
| $query = PaymentPlan::getQuery() | |||||
| ->where(PaymentPlan::FIELD_CUSTOMER_CODE, $this->pool->customerCode) | |||||
| ->whereNull(PaymentPlan::FIELD_APPROPRIATION_DATE) | |||||
| ->where(function (KintoneRecordQuery $q) { | |||||
| $q->whereNull(PaymentPlan::FIELD_APPROPRIATION_AMOUNT) | |||||
| ->orWhere(PaymentPlan::FIELD_APPROPRIATION_AMOUNT, 0); | |||||
| }) | |||||
| ->orderByAsc(PaymentPlan::FIELD_PAYMENT_PLAN_DATE); | |||||
| $plans = PaymentPlan::getAccess()->all($query); | |||||
| return $plans; | |||||
| } | |||||
| private function canAttach(PaymentPlan $plan) | |||||
| { | |||||
| $afterPool = $this->pool->poolAmount - $plan->paymentPlanAmount; | |||||
| return 0 <= $afterPool; | |||||
| } | |||||
| private function makeHistory(PaymentPlan $plan) | |||||
| { | |||||
| $history = new PoolTransferHistory(); | |||||
| $history->poolRecordNo = $this->pool->getRecordId(); | |||||
| $history->customerCode = $this->pool->customerCode; | |||||
| $history->transferDatetime = DateUtil::now(); | |||||
| $history->transferType = TransgerType::ATTACH; | |||||
| $history->transferAmount = $plan->paymentPlanAmount; | |||||
| $history->poolAmountBefore = $this->pool->poolAmount; | |||||
| $history->poolAmountAfter = $this->pool->poolAmount - $plan->paymentPlanAmount; | |||||
| $history->paymentPlanRecordNo = $plan->getRecordId(); | |||||
| $this->histories->push($history); | |||||
| } | |||||
| private function setPoolAmount(PaymentPlan $plan) | |||||
| { | |||||
| $afterPool = $this->pool->poolAmount - $plan->paymentPlanAmount; | |||||
| $this->pool->poolAmount = $afterPool; | |||||
| } | |||||
| private function setPaymentPlanDone(PaymentPlan $plan) | |||||
| { | |||||
| $plan->appropriationDate = DateUtil::now(); | |||||
| $plan->appropriationAmount = $plan->paymentPlanAmount; | |||||
| $plan->remainingAmount = 0; | |||||
| if (!!$plan->firstPaymentEntryRecordNo) { | |||||
| $this->firstPaymnetEntryRecordIds->put($plan->firstPaymentEntryRecordNo, true); | |||||
| } | |||||
| $this->completePlans->push($plan); | |||||
| } | |||||
| public function save() | |||||
| { | |||||
| foreach ($this->histories as $history) { | |||||
| $history->save(); | |||||
| } | |||||
| foreach ($this->completePlans as $plan) { | |||||
| $plan->save(); | |||||
| } | |||||
| $this->pool->save(); | |||||
| $this->makeReceipt(); | |||||
| return $this; | |||||
| } | |||||
| private function makeReceipt() | |||||
| { | |||||
| if ($this->completePlans->isEmpty()) { | |||||
| return; | |||||
| } | |||||
| // 支払い種別ごとにグルーピング | |||||
| $ids = []; | |||||
| foreach ($this->completePlans as $plan) { | |||||
| // 保証金は領収証を発行しない | |||||
| if ($plan->paymentType === PaymentType::A保証金) { | |||||
| continue; | |||||
| } | |||||
| $ids[$plan->paymentType][] = $plan; | |||||
| } | |||||
| // 領収証作成 | |||||
| foreach ($ids as $targets) { | |||||
| $manager = new ReceiptManager(); | |||||
| $manager->setPaymentPlans(collect($targets)) | |||||
| ->create($targets); | |||||
| } | |||||
| return $this; | |||||
| } | |||||
| private function clear() | |||||
| { | |||||
| $this->pool = null; | |||||
| $this->histories = collect(); | |||||
| $this->firstPaymnetEntryRecordIds = collect(); | |||||
| $this->plans = collect(); | |||||
| $this->completePlans = collect(); | |||||
| } | |||||
| } | |||||
| class AttachStop extends Exception | |||||
| { | |||||
| } | |||||
| @@ -0,0 +1,119 @@ | |||||
| <?php | |||||
| namespace App\Logic; | |||||
| use App\Exceptions\SkipException; | |||||
| use App\Kintone\Models\DropDown\PoolTransferHistory\TransferMethod; | |||||
| use App\Kintone\Models\DropDown\PoolTransferHistory\TransgerType; | |||||
| use App\Kintone\Models\Pool; | |||||
| use App\Kintone\Models\PoolTransferHistory; | |||||
| use App\Kintone\Models\SmbcAccountTransferResult; | |||||
| use App\Kintone\Models\SmbcPayment; | |||||
| use App\Kintone\Models\YuchoPaymentResult; | |||||
| use App\Util\DateUtil; | |||||
| /** | |||||
| * 各種支払いデータから入金プールへ異動する | |||||
| */ | |||||
| class PoolTransferManager | |||||
| { | |||||
| private SmbcPayment|SmbcAccountTransferResult|YuchoPaymentResult|null $payment = null; | |||||
| private Pool|null $pool = null; | |||||
| private PoolTransferHistory|null $history = null; | |||||
| public function moveToPool(SmbcPayment|SmbcAccountTransferResult|YuchoPaymentResult $payment): Pool | |||||
| { | |||||
| $this->clear(); | |||||
| if (!$payment->customerCode) { | |||||
| throw new SkipException(sprintf("顧客コード未割当 レコード番号:%d", $payment->getRecordId())); | |||||
| } | |||||
| $this->payment = $payment; | |||||
| $this->getPool(); | |||||
| $this->setPoolDone(); | |||||
| $this->makeHistory(); | |||||
| $this->setPoolAmount(); | |||||
| $this->save(); | |||||
| return $this->pool; | |||||
| } | |||||
| private function getPool() | |||||
| { | |||||
| $access = Pool::getAccess(); | |||||
| $query = Pool::getQuery()->where(Pool::FIELD_CUSTOMER_CODE, $this->payment->customerCode); | |||||
| $list = $access->some($query); | |||||
| if ($list->isNotEmpty()) { | |||||
| if ($list->count() !== 1) { | |||||
| throw new SkipException(sprintf("データ不正 入金プール一意制約違反 顧客コード%d", $this->payment->customerCode)); | |||||
| } | |||||
| $this->pool = $list->first(); | |||||
| return; | |||||
| } | |||||
| $pool = new Pool(); | |||||
| $pool->customerCode = $this->payment->customerCode; | |||||
| $pool->poolAmount = 0; | |||||
| $pool->save(); | |||||
| $this->pool = $pool; | |||||
| } | |||||
| private function makeHistory() | |||||
| { | |||||
| $history = new PoolTransferHistory(); | |||||
| $history->poolRecordNo = $this->pool->getRecordId(); | |||||
| $history->customerCode = $this->payment->customerCode; | |||||
| $history->transferDatetime = DateUtil::now(); | |||||
| $history->transferType = TransgerType::INCOME; | |||||
| $history->transferAmount = $this->payment->paymentAmount; | |||||
| $history->poolAmountBefore = $this->pool->poolAmount; | |||||
| $history->poolAmountAfter = $this->pool->poolAmount + $this->payment->paymentAmount; | |||||
| if ($this->payment instanceof SmbcPayment) { | |||||
| $history->incomeMethod = TransferMethod::CVS; | |||||
| $history->incomeCvsPaymentRecordNo = $this->payment->getRecordId(); | |||||
| } | |||||
| if ($this->payment instanceof SmbcAccountTransferResult) { | |||||
| $history->incomeMethod = TransferMethod::ACCOUNT_TRANSFER; | |||||
| $history->incomeAccountTransferResultRecordNo = $this->payment->getRecordId(); | |||||
| } | |||||
| if ($this->payment instanceof YuchoPaymentResult) { | |||||
| $history->incomeMethod = TransferMethod::YUCHO; | |||||
| $history->incomeYuchoTransferRecordNo = $this->payment->getRecordId(); | |||||
| } | |||||
| $this->history = $history; | |||||
| } | |||||
| private function setPoolDone() | |||||
| { | |||||
| $this->payment->poolDone = ["済"]; | |||||
| } | |||||
| private function setPoolAmount() | |||||
| { | |||||
| $this->pool->poolAmount += $this->payment->paymentAmount; | |||||
| } | |||||
| private function save() | |||||
| { | |||||
| $this->history->save(); | |||||
| $this->pool->save(); | |||||
| $this->payment->save(); | |||||
| } | |||||
| private function clear() | |||||
| { | |||||
| $this->payment = null; | |||||
| $this->pool = null; | |||||
| $this->history = null; | |||||
| } | |||||
| } | |||||
| @@ -22,6 +22,8 @@ class ReceiptManager | |||||
| { | { | ||||
| private ?Receipt $receipt = null; | private ?Receipt $receipt = null; | ||||
| private $paymentPlans = null; | |||||
| public function __construct(?int $recordNo = null) | public function __construct(?int $recordNo = null) | ||||
| { | { | ||||
| if ($recordNo) { | if ($recordNo) { | ||||
| @@ -36,6 +38,12 @@ class ReceiptManager | |||||
| return $this->receipt; | return $this->receipt; | ||||
| } | } | ||||
| public function setPaymentPlans(Collection $paymentPlans) | |||||
| { | |||||
| $this->paymentPlans = $paymentPlans; | |||||
| return $this; | |||||
| } | |||||
| public function create(array $paymentPlanRecordNoList) | public function create(array $paymentPlanRecordNoList) | ||||
| { | { | ||||
| @@ -67,7 +75,7 @@ class ReceiptManager | |||||
| $detail->amount = $detail->unitPrice * $detail->quantity; | $detail->amount = $detail->unitPrice * $detail->quantity; | ||||
| $detail->targetMonth = $payment->targetMonth ? sprintf("%d月分", $payment->targetMonth) : ""; | $detail->targetMonth = $payment->targetMonth ? sprintf("%d月分", $payment->targetMonth) : ""; | ||||
| $detail->taxRate = config("business.taxRate.normal", 0); | $detail->taxRate = config("business.taxRate.normal", 0); | ||||
| $detail->memo = "memo"; | |||||
| $detail->memo = $this->getMemoContents($payment); | |||||
| $receipt->receiptDetail->push($detail); | $receipt->receiptDetail->push($detail); | ||||
| $receipt->receiptTotalAmount += $payment->appropriationAmount; | $receipt->receiptTotalAmount += $payment->appropriationAmount; | ||||
| } | } | ||||
| @@ -112,6 +120,9 @@ class ReceiptManager | |||||
| private function getPayments(array $paymentPlanRecordNoList) | private function getPayments(array $paymentPlanRecordNoList) | ||||
| { | { | ||||
| if (!!$this->paymentPlans) { | |||||
| return $this->paymentPlans; | |||||
| } | |||||
| $query = PaymentPlan::getQuery()->whereIn(PaymentPlan::FIELD_RECORD_NO, $paymentPlanRecordNoList); | $query = PaymentPlan::getQuery()->whereIn(PaymentPlan::FIELD_RECORD_NO, $paymentPlanRecordNoList); | ||||
| $ret = PaymentPlan::getAccess()->all($query); | $ret = PaymentPlan::getAccess()->all($query); | ||||
| @@ -286,4 +297,13 @@ class ReceiptManager | |||||
| } | } | ||||
| throw new AppCommonException('申請番号取得失敗'); | throw new AppCommonException('申請番号取得失敗'); | ||||
| } | } | ||||
| private function getMemoContents(PaymentPlan $plan): string | |||||
| { | |||||
| if ($plan->isPartialFee()) { | |||||
| return "日割り分"; | |||||
| } | |||||
| return ""; | |||||
| } | |||||
| } | } | ||||
| @@ -0,0 +1,15 @@ | |||||
| <?php | |||||
| namespace App\Util; | |||||
| use Illuminate\Support\Collection; | |||||
| class CollectionUtil | |||||
| { | |||||
| public static function pushAll($base, $append) | |||||
| { | |||||
| foreach ($append as $ele) { | |||||
| $base->push($ele); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -50,6 +50,10 @@ return [ | |||||
| ...App\Kintone\Models\Ask::setConfig(), | ...App\Kintone\Models\Ask::setConfig(), | ||||
| ...App\Kintone\Models\Receipt::setConfig(), | ...App\Kintone\Models\Receipt::setConfig(), | ||||
| ...App\Kintone\Models\SmbcPayment::setConfig(), | ...App\Kintone\Models\SmbcPayment::setConfig(), | ||||
| ...App\Kintone\Models\SmbcAccountTransferResult::setConfig(), | |||||
| ...App\Kintone\Models\YuchoPaymentResult::setConfig(), | |||||
| ...App\Kintone\Models\Pool::setConfig(), | |||||
| ...App\Kintone\Models\PoolTransferHistory::setConfig(), | |||||
| ], | ], | ||||
| ]; | ]; | ||||