| @@ -4,6 +4,7 @@ namespace App\Codes; | |||
| enum EnvironmentName: string | |||
| { | |||
| case TEST = 'testing'; | |||
| case LOCAL = 'local'; | |||
| case STAGING = 'staging'; | |||
| case PRODUCTOIN = 'production'; | |||
| @@ -0,0 +1,186 @@ | |||
| <?php | |||
| namespace App\Console\Commands; | |||
| use App\Http\API\SMBC\PollResultRecord; | |||
| use App\Http\API\SMBC\SMBC; | |||
| use App\Http\API\SMBC\SMBCStatus; | |||
| use App\Kintone\Models\BankAccountUpdateApplication; | |||
| use App\Kintone\Models\Customer; | |||
| use App\Logic\GeneralApplicationManager; | |||
| use App\Models\SmbcPollStatus; | |||
| use App\Util\DateUtil; | |||
| use App\Util\DBUtil; | |||
| use Exception; | |||
| use Illuminate\Support\Carbon; | |||
| use Illuminate\Support\Collection; | |||
| use Illuminate\Support\Facades\DB; | |||
| class SMBCPoll extends BaseCommand | |||
| { | |||
| const COMMAND = "smbc:poll"; | |||
| /** | |||
| * The name and signature of the console command. | |||
| * | |||
| * @var string | |||
| */ | |||
| protected $signature = self::COMMAND; | |||
| /** | |||
| * The console command description. | |||
| * | |||
| * @var string | |||
| */ | |||
| protected $description = 'SMBCへ口座振替登録申請結果を取得する'; | |||
| static public function getCommand() | |||
| { | |||
| return self::COMMAND; | |||
| } | |||
| /** | |||
| * @var Collection<int, BankAccountUpdateApplication> | |||
| */ | |||
| private Collection $applications; | |||
| /** | |||
| * Create a new command instance. | |||
| * | |||
| * @return void | |||
| */ | |||
| public function __construct() | |||
| { | |||
| parent::__construct(); | |||
| $this->applications = collect(); | |||
| } | |||
| /** | |||
| * Execute the console command. | |||
| * | |||
| * @return int | |||
| */ | |||
| public function service(): int | |||
| { | |||
| try { | |||
| $db = DBUtil::instance(); | |||
| $db->beginTransaction(); | |||
| // 検索範囲の取得 | |||
| [$from, $to] = $this->getFromTo(); | |||
| $this->outputInfo(sprintf("検索範囲 %s-%s", $from->format('Y/m/d H:i:s'), $to->format('Y/m/d H:i:s'))); | |||
| // 検索実行 | |||
| $result = SMBC::poll($from, $to); | |||
| // 検索結果の確認 | |||
| if (!$result->ok()) { | |||
| $this->outputError($result->getMessage()); | |||
| return self::RESULTCODE_FAILED; | |||
| } | |||
| $this->outputInfo(sprintf("取得対象 %d件", $result->getCount())); | |||
| // データハンドリング | |||
| foreach ($result->getRecord() as $data) { | |||
| $this->handleData($data); | |||
| } | |||
| // 検索実績の登録 | |||
| $this->saveFromTo($from, $to); | |||
| // キントーンへ各種申請登録 | |||
| foreach ($this->applications as $app) { | |||
| $this->outputInfo(sprintf("申請登録 顧客コード:%s 申請番号:%s", $app->customerCode, $app->applicationNo)); | |||
| $app->save(); | |||
| } | |||
| $this->outputInfo(sprintf("申請登録件数:%d件", $this->applications->count())); | |||
| $db->commit(); | |||
| } catch (Exception $e) { | |||
| $db->rollBack(); | |||
| throw $e; | |||
| } | |||
| return self::RESULTCODE_SUCCESS; | |||
| } | |||
| /** | |||
| * @return Carbon[] | |||
| */ | |||
| private function getFromTo() | |||
| { | |||
| $status = SmbcPollStatus::all()->first(); | |||
| $now = DateUtil::now(); | |||
| if ($status === null) { | |||
| $this->outputInfo("検索範囲初期化"); | |||
| return [ | |||
| $now->clone()->addDays(-5), | |||
| $now->clone(), | |||
| ]; | |||
| } | |||
| return [ | |||
| $status->condition_datetime_to->clone()->addSecond(), | |||
| $now->clone(), | |||
| ]; | |||
| } | |||
| private function handleData(PollResultRecord $data) | |||
| { | |||
| if ($data->status === SMBCStatus::SUCCESS) { | |||
| $customer = Customer::findByCustomerCode($data->getCustomerCode()); | |||
| $application = new BankAccountUpdateApplication(); | |||
| $manager = new GeneralApplicationManager($application); | |||
| $manager | |||
| ->setCustomer($customer) | |||
| ->makeApplication(); | |||
| $application->bankBranchIdBefore = $customer->bankBranchId; | |||
| $application->bankBranchIdAfter = $data->bankBranchCode; | |||
| $application->bankCodeAfter = $data->bankCode; | |||
| $application->bankNameAfter = $data->bankName; | |||
| $application->branchCodeAfter = $data->branchCode; | |||
| $application->branchNameAfter = $data->branchName; | |||
| $application->accountTypeAfter = $data->accountType; | |||
| $application->accountNameKanaAfter = $data->accountName; | |||
| $application->accountNoAfter = $data->accountNo; | |||
| $application->accountYuchoSignAfter = $data->yuchoSign; | |||
| $application->accountYuchoNoAfter = $data->yuchoAccountNo; | |||
| $application->smbcApplicationDatetime = $data->applicationDatetime; | |||
| $application->smbcAcceptNo = $data->acceptNo; | |||
| $application->smbcResult = $data->all; | |||
| $this->applications->push($application); | |||
| return; | |||
| } | |||
| if ($data->status === SMBCStatus::ERROR || $data->status === SMBCStatus::CANCEL) { | |||
| // TODOエラーメール送信 | |||
| return; | |||
| } | |||
| if ($data->status === SMBCStatus::PROCESSING) { | |||
| $this->outputInfo(sprintf("処理中のためスキップ 受付番号%s", $data->acceptNo)); | |||
| return; | |||
| } | |||
| if ($data->address5 !== SMBC::CONDITION_ADDR5_FROM_MY_PAGE) { | |||
| $this->outputInfo(sprintf("MyPage以外からの申請のためスキップ 受付番号%s", $data->acceptNo)); | |||
| return; | |||
| } | |||
| } | |||
| private function saveFromTo(Carbon $from, Carbon $to) | |||
| { | |||
| DB::table(SmbcPollStatus::getTableName())->delete(); | |||
| $status = new SmbcPollStatus(); | |||
| $status->condition_datetime_to = $to; | |||
| $status->save(); | |||
| } | |||
| } | |||
| @@ -13,6 +13,7 @@ class Kernel extends ConsoleKernel | |||
| protected function schedule(Schedule $schedule): void | |||
| { | |||
| Schedules\HeartBeat::register($schedule); | |||
| Schedules\SMBCPoll::register($schedule); | |||
| } | |||
| /** | |||
| @@ -0,0 +1,17 @@ | |||
| <?php | |||
| namespace App\Console\Schedules; | |||
| use App\Console\Commands\SMBCPoll as CommandsSMBCPoll; | |||
| use Illuminate\Console\Scheduling\Schedule; | |||
| class SMBCPoll extends BaseSchedule | |||
| { | |||
| static public function register(Schedule $schedule) | |||
| { | |||
| $schedule->command(CommandsSMBCPoll::class) | |||
| ->everyThreeMinutes() | |||
| ->description("SMBC口座振替申請結果取得"); | |||
| } | |||
| } | |||
| @@ -0,0 +1,147 @@ | |||
| <?php | |||
| namespace App\Http\API\SMBC; | |||
| use App\Codes\EnvironmentName; | |||
| use App\Util\EncodingUtil; | |||
| use Exception; | |||
| use Illuminate\Support\Arr; | |||
| use Illuminate\Support\Carbon; | |||
| use Illuminate\Support\Collection; | |||
| use Illuminate\Support\Facades\Http; | |||
| use Illuminate\Support\Str; | |||
| class PollResult | |||
| { | |||
| // 共通 | |||
| private const IDX_RECORD_CODE = 0; | |||
| // ヘッダー | |||
| private const IDX_HEADER_RESULT = 1; | |||
| private const IDX_HEADER_MESSAGE = 2; | |||
| // ボディー | |||
| private const IDX_FOOTER_COUNT = 1; | |||
| // フッター | |||
| private const RECORD_CODE_HEADER = "10"; | |||
| private const RECORD_CODE_BODY = "20"; | |||
| private const RECORD_CODE_FOOTER = "80"; | |||
| private const RESULT_CODE_SUCCESS = ["000000", "943037"]; | |||
| /** | |||
| * @var Collection<int, Collection<int, string>> | |||
| */ | |||
| private Collection $lines; | |||
| /** | |||
| * @var Collection<int, PollResultRecord> | |||
| */ | |||
| private Collection $body; | |||
| private bool $success = false; | |||
| private string $message = ""; | |||
| private int $count = 0; | |||
| public function __construct(string $data) | |||
| { | |||
| $this->lines = collect(); | |||
| $lines = Str::of($data)->replace('"', '')->explode("\r\n"); | |||
| foreach ($lines as $lineStr) { | |||
| if ($lineStr) { | |||
| $lineStr = EncodingUtil::toUtf8FromSjis($lineStr); | |||
| $this->lines->push(Str::of($lineStr)->explode(',')); | |||
| } | |||
| } | |||
| if (!$this->parseHeader()) { | |||
| return; | |||
| } | |||
| if (!$this->parseBody()) { | |||
| return; | |||
| } | |||
| if (!$this->parseFooter()) { | |||
| return; | |||
| } | |||
| $this->success = true; | |||
| } | |||
| public function ok(): bool | |||
| { | |||
| return $this->success; | |||
| } | |||
| public function getMessage(): string | |||
| { | |||
| return $this->message; | |||
| } | |||
| public function getRecord() | |||
| { | |||
| return $this->body; | |||
| } | |||
| public function getCount(): int | |||
| { | |||
| return $this->count; | |||
| } | |||
| private function parseHeader(): bool | |||
| { | |||
| $header = $this->lines->first(); | |||
| if (!$header) { | |||
| $this->success = false; | |||
| $this->message = "ヘッダーなし"; | |||
| return false; | |||
| } | |||
| $resultCode = $header->get(self::IDX_HEADER_RESULT); | |||
| if (!in_array($resultCode, self::RESULT_CODE_SUCCESS)) { | |||
| $this->success = false; | |||
| $this->message = sprintf("結果コードNG %s", $resultCode); | |||
| $readMessage = EncodingUtil::toUtf8FromSjis($header->get(self::IDX_HEADER_MESSAGE)); | |||
| return false; | |||
| } | |||
| return true; | |||
| } | |||
| private function parseBody(): bool | |||
| { | |||
| $this->body = collect(); | |||
| try { | |||
| foreach ($this->lines as $line) { | |||
| if ($line[self::IDX_RECORD_CODE] === self::RECORD_CODE_BODY) { | |||
| $this->body->push(new PollResultRecord($line)); | |||
| } | |||
| } | |||
| } catch (Exception $e) { | |||
| $this->success = false; | |||
| $this->message = sprintf("Bodyパース失敗 %s", $e->getMessage()); | |||
| return false; | |||
| } | |||
| return true; | |||
| } | |||
| private function parseFooter(): bool | |||
| { | |||
| $footer = $this->lines->last(); | |||
| if (!$footer) { | |||
| $this->success = false; | |||
| $this->message = "フッターなし"; | |||
| return false; | |||
| } | |||
| $count = intval($footer->get(self::IDX_FOOTER_COUNT)); | |||
| if ($count !== $this->body->count()) { | |||
| $this->success = false; | |||
| $this->message = sprintf("読込件数に差異あり %d : %d", $count, $this->body->count()); | |||
| return false; | |||
| } | |||
| $this->count = $count; | |||
| return true; | |||
| } | |||
| } | |||
| @@ -0,0 +1,98 @@ | |||
| <?php | |||
| namespace App\Http\API\SMBC; | |||
| use App\Util\DateUtil; | |||
| use Illuminate\Support\Carbon; | |||
| use Illuminate\Support\Collection; | |||
| use Illuminate\Support\Str; | |||
| class PollResultRecord | |||
| { | |||
| private const IDX_ACCEPT_NO = 11; | |||
| private const IDX_APPLICATION_DATE = 12; | |||
| private const IDX_APPLICATION_TIME = 13; | |||
| private const IDX_CUSTOMER_NO = 22; | |||
| private const IDX_ADDRESS5 = 33; | |||
| private const IDX_STATUS = 40; | |||
| private const IDX_BANK_CODE = 60; | |||
| private const IDX_BANK_NAME = 61; | |||
| private const IDX_BRANCH_CODE = 62; | |||
| private const IDX_BRANCH_NAME = 63; | |||
| private const IDX_ACCOUNT_TYPE = 64; | |||
| private const IDX_ACCOUNT_NO = 66; | |||
| private const IDX_ACCOUNT_NAME = 67; | |||
| private const BANK_CODE_YUCHO = "9900"; | |||
| public string $acceptNo = ""; // 口座振替受付番号 | |||
| public Carbon $applicationDatetime; // 申込日時 | |||
| public string $customerNo = ""; // 顧客番号 | |||
| public SMBCStatus $status; | |||
| public string $bankCode = ""; // 金融機関コード | |||
| public string $bankName = ""; | |||
| public string $branchCode = ""; // 支店コード | |||
| public string $branchName = ""; | |||
| public string $accountType = ""; // 預金種目 | |||
| public string $accountNo = ""; // 口座番号 | |||
| public string $accountName = ""; // 口座名義カナ | |||
| public string $yuchoSign = ""; // ゆうちょ口座記号 | |||
| public string $yuchoAccountNo = ""; // ゆうちょ口座番号 | |||
| public string $bankBranchCode = ""; // 銀行支店コード | |||
| public string $address5 = ""; // 住所5 | |||
| public string $all = ""; | |||
| /** | |||
| * | |||
| * @param Collection<int, string> $data | |||
| */ | |||
| public function __construct(Collection $data) | |||
| { | |||
| $this->acceptNo = $data[self::IDX_ACCEPT_NO]; | |||
| $this->applicationDatetime = DateUtil::parse($data[self::IDX_APPLICATION_DATE] . $data[self::IDX_APPLICATION_TIME]); | |||
| $this->customerNo = $data[self::IDX_CUSTOMER_NO]; | |||
| $this->address5 = $data[self::IDX_ADDRESS5]; | |||
| $this->status = SMBCStatus::from($data[self::IDX_STATUS]); | |||
| $this->bankCode = $data[self::IDX_BANK_CODE]; | |||
| $this->bankName = $data[self::IDX_BANK_NAME]; | |||
| $this->branchCode = $data[self::IDX_BRANCH_CODE]; | |||
| $this->branchName = $data[self::IDX_BRANCH_NAME]; | |||
| $this->accountType = $data[self::IDX_ACCOUNT_TYPE]; | |||
| $this->accountNo = $data[self::IDX_ACCOUNT_NO]; | |||
| $this->accountName = $data[self::IDX_ACCOUNT_NAME]; | |||
| // ゆうちょの場合、 | |||
| // 支店コードに記号5桁のうちの3桁 | |||
| // 口座番号に記号8桁のうちの7桁が入っているので | |||
| // 調整する | |||
| if ($this->bankCode === self::BANK_CODE_YUCHO) { | |||
| $branchCode = $this->branchCode; | |||
| $accountNo = $this->accountNo; | |||
| $this->branchCode = ""; | |||
| $this->accountNo = ""; | |||
| // 記号には前後に1,0を付与する | |||
| $this->yuchoSign = sprintf("1%s0", $branchCode); | |||
| // 口座番号には末尾に1を付与する | |||
| $this->yuchoAccountNo = sprintf("%s1", $accountNo); | |||
| // 支店コードは記号の前2桁に8を付与する | |||
| $this->branchCode = sprintf("%s8", Str::of($branchCode)->substr(2)); | |||
| } | |||
| $this->bankBranchCode = sprintf("%d%s", intval($this->bankCode), $this->branchCode); | |||
| $this->all = $data->implode(","); | |||
| } | |||
| public function getCustomerCode(): int | |||
| { | |||
| return intval($this->customerNo); | |||
| } | |||
| } | |||
| @@ -0,0 +1,100 @@ | |||
| <?php | |||
| namespace App\Http\API\SMBC; | |||
| use App\Exceptions\ConfigException; | |||
| use App\Kintone\Models\Customer; | |||
| use Illuminate\Support\Carbon; | |||
| use Illuminate\Support\Facades\Http; | |||
| class SMBC | |||
| { | |||
| const CONDITION_ADDR5_FROM_MY_PAGE = "マイページからの申請"; | |||
| public static function poll(Carbon $from, Carbon $to) | |||
| { | |||
| $url = config('smbc.searchUrl'); | |||
| if (!$url) { | |||
| throw new ConfigException("smbc.searchUrl", $url); | |||
| } | |||
| $password = config('smbc.searchPassword'); | |||
| if (!$password) { | |||
| throw new ConfigException("smbc.searchPassword", $password); | |||
| } | |||
| $sendData = [ | |||
| 'version' => "213", | |||
| 'shori_kbn' => "2001", | |||
| 'shop_cd' => "7694156", | |||
| 'syuno_co_cd' => "58763", | |||
| 'shop_pwd' => $password, | |||
| // 口座振替受付ステータス更新日時のFROM-TO検索条件 | |||
| 'kfr_utk_status_upd_date_from' => $from->format('Ymd'), | |||
| 'kfr_utk_status_upd_time_from' => $from->format('His'), | |||
| 'kfr_utk_status_upd_date_to' => $to->format('Ymd'), | |||
| 'kfr_utk_status_upd_time_to' => $to->format('His'), | |||
| // ソート指定 | |||
| 'sort_list' => "11", // 口座振替受付ステータス更新日時 | |||
| 'sort_jun' => "2" // 降順 | |||
| ]; | |||
| $res = Http::withHeaders([ | |||
| 'Content-Type' => 'application/x-www-form-urlencoded', | |||
| 'Content-Encoding' => 'Shift_JIS' | |||
| ])->asForm()->post( | |||
| $url, | |||
| $sendData | |||
| ); | |||
| if ($res->failed()) { | |||
| throw $res->toException(); | |||
| } | |||
| return new PollResult($res->body()); | |||
| } | |||
| public static function getRegisterStartParam(Customer $customer) | |||
| { | |||
| $password = config('smbc.systemPassword'); | |||
| if (!$password) { | |||
| throw new ConfigException('smbc.systemPassword', $password); | |||
| } | |||
| $url = config('smbc.registerUrl'); | |||
| if (!$url) { | |||
| throw new ConfigException('smbc.registerUrl', $url); | |||
| } | |||
| $param = [ | |||
| 'bill_no' => sprintf("%012d", $customer->customerCode), | |||
| 'bill_name' => $customer->customerName, | |||
| 'bill_kana' => mb_convert_kana($customer->customerNameKana, "ks"), | |||
| 'version' => "130", | |||
| 'bill_method' => "01", | |||
| 'shop_cd' => "7694156", | |||
| 'syuno_co_cd' => "58763", | |||
| 'shop_pwd' => $password, | |||
| 'shoporder_no' => "", | |||
| 'koushin_kbn' => "1", | |||
| 'shop_phon_hyoji_kbn' => "1", | |||
| 'shop_mail_hyoji_kbn' => "1", | |||
| 'kessai_id' => "0101", | |||
| 'bill_adr_5' => self::CONDITION_ADDR5_FROM_MY_PAGE, | |||
| ]; | |||
| $param['fs'] = hash('sha256', $param['shop_cd'] . $param['syuno_co_cd'] . $param['bill_no'] . $param['shoporder_no'] . $param['shop_pwd']); | |||
| $data = [ | |||
| 'url' => $url, | |||
| 'param' => $param, | |||
| ]; | |||
| return $data; | |||
| } | |||
| } | |||
| @@ -0,0 +1,11 @@ | |||
| <?php | |||
| namespace App\Http\API\SMBC; | |||
| enum SMBCStatus: string | |||
| { | |||
| case PROCESSING = "01"; | |||
| case SUCCESS = "02"; | |||
| case ERROR = "03"; | |||
| case CANCEL = "04"; | |||
| } | |||
| @@ -0,0 +1,40 @@ | |||
| <?php | |||
| namespace App\Http\Controllers\Web\Customer; | |||
| use App\Exceptions\ConfigException; | |||
| use App\Http\API\SMBC\SMBC; | |||
| use App\Http\Controllers\Web\WebController; | |||
| use App\Kintone\Models\Customer; | |||
| use Illuminate\Http\JsonResponse; | |||
| use Illuminate\Http\Request; | |||
| class BankAccountRegisterStartController extends WebController | |||
| { | |||
| public function name(): string | |||
| { | |||
| return "利用者口座変更申請開始"; | |||
| } | |||
| public function description(): string | |||
| { | |||
| return "利用者口座変更申請開始用のパラメータを取得する"; | |||
| } | |||
| public function __construct(protected BankAccountRegisterStartParam $param) | |||
| { | |||
| parent::__construct(); | |||
| $this->middleware('auth:sanctum'); | |||
| } | |||
| protected function run(Request $request): JsonResponse | |||
| { | |||
| $customer = Customer::getSelf(); | |||
| $data = SMBC::getRegisterStartParam($customer); | |||
| return $this->successResponse($data); | |||
| } | |||
| } | |||
| @@ -0,0 +1,15 @@ | |||
| <?php | |||
| namespace App\Http\Controllers\Web\Customer; | |||
| use App\Http\Controllers\Web\BaseParam; | |||
| /** | |||
| */ | |||
| class BankAccountRegisterStartParam extends BaseParam | |||
| { | |||
| public function rules(): array | |||
| { | |||
| return []; | |||
| } | |||
| } | |||
| @@ -2,6 +2,7 @@ | |||
| namespace App\Http\Controllers\Web; | |||
| use App\Codes\EnvironmentName; | |||
| use App\Codes\HTTPResultCode as ResultCode; | |||
| use App\Codes\UserRole; | |||
| use App\Exceptions\AppCommonException; | |||
| @@ -301,7 +302,13 @@ abstract class WebController extends BaseController | |||
| ->json($ret) | |||
| ->withHeaders($this->makeHeader()); | |||
| } else { | |||
| abort(500); | |||
| if (app()->environment([EnvironmentName::PRODUCTOIN->value])) { | |||
| abort(500); | |||
| } | |||
| return response() | |||
| ->json($ret) | |||
| ->withHeaders($this->makeHeader()); | |||
| } | |||
| } | |||
| @@ -28,7 +28,7 @@ class KintoneRecordQuery | |||
| $ret .= " "; | |||
| } | |||
| $ret .= $this->order; | |||
| logger(sprintf("QUERY[%s]:%s", $this->appName, $ret)); | |||
| // logger(sprintf("QUERY[%s]:%s", $this->appName, $ret)); | |||
| return $ret; | |||
| } | |||
| @@ -0,0 +1,13 @@ | |||
| <?php | |||
| namespace App\Kintone\Models; | |||
| use Illuminate\Support\Facades\Auth; | |||
| /** | |||
| * アプリ名 銀行マスタ | |||
| */ | |||
| class Bank extends KintoneModel | |||
| { | |||
| const CONFIG_KEY = "KINTONE_APP_BANK"; | |||
| } | |||
| @@ -0,0 +1,93 @@ | |||
| <?php | |||
| namespace App\Kintone\Models; | |||
| use Illuminate\Support\Carbon; | |||
| /** | |||
| * アプリ名 各種申請 [口座変更申請] | |||
| * | |||
| * @property string bankBranchIdBefore | |||
| * @property string bankCodeBefore | |||
| * @property string bankNameBefore | |||
| * @property string branchCodeBefore | |||
| * @property string branchNameBefore | |||
| * @property int accountTypeBefore | |||
| * @property string accountNameKanaBefore | |||
| * @property int accountNoBefore | |||
| * @property int accountYuchoSignBefore | |||
| * @property int accountYuchoNoBefore | |||
| * @property string bankBranchIdAfter | |||
| * @property string bankCodeAfter | |||
| * @property string bankNameAfter | |||
| * @property string branchCodeAfter | |||
| * @property string branchNameAfter | |||
| * @property int accountTypeAfter | |||
| * @property string accountNameKanaAfter | |||
| * @property int accountNoAfter | |||
| * @property int accountYuchoSignAfter | |||
| * @property int accountYuchoNoAfter | |||
| * @property Carbon smbcApplicationDatetime | |||
| * @property string smbcAcceptNo | |||
| * @property string smbcResult | |||
| */ | |||
| class BankAccountUpdateApplication extends GeneralApplication | |||
| { | |||
| const FIELD_BANK_BRANCH_ID_BEFORE = "口座変更申請_変更前_銀行支店ID"; | |||
| const FIELD_BANK_CODE_BEFORE = "口座変更申請_変更前_金融機関コード"; | |||
| const FIELD_BANK_NAME_BEFORE = "口座変更申請_変更前_金融機関名"; | |||
| const FIELD_BRANCH_CODE_BEFORE = "口座変更申請_変更前_支店コード"; | |||
| const FIELD_BRANCH_NAME_BEFORE = "口座変更申請_変更前_支店名"; | |||
| const FIELD_ACCOUNT_TYPE_BEFORE = "口座変更申請_変更前_口座種目"; | |||
| const FIELD_ACCOUNT_NAME_KANA_BEFORE = "口座変更申請_変更前_口座名義カナ"; | |||
| const FIELD_ACCOUNT_NO_BEFORE = "口座変更申請_変更前_口座番号"; | |||
| const FIELD_ACCOUNT_YUCHO_SIGN_BEFORE = "口座変更申請_変更前_ゆうちょ口座記号"; | |||
| const FIELD_ACCOUNT_YUCHO_NO_BEFORE = "口座変更申請_変更前_ゆうちょ口座番号"; | |||
| const FIELD_BANK_BRANCH_ID_AFTER = "口座変更申請_変更後_銀行支店ID"; | |||
| const FIELD_BANK_CODE_AFTER = "口座変更申請_変更後_金融機関コード"; | |||
| const FIELD_BANK_NAME_AFTER = "口座変更申請_変更後_金融機関名"; | |||
| const FIELD_BRANCH_CODE_AFTER = "口座変更申請_変更後_支店コード"; | |||
| const FIELD_BRANCH_NAME_AFTER = "口座変更申請_変更後_支店名"; | |||
| const FIELD_ACCOUNT_TYPE_AFTER = "口座変更申請_変更後_口座種目"; | |||
| const FIELD_ACCOUNT_NAME_KANA_AFTER = "口座変更申請_変更後_口座名義カナ"; | |||
| const FIELD_ACCOUNT_NO_AFTER = "口座変更申請_変更後_口座番号"; | |||
| const FIELD_ACCOUNT_YUCHO_SIGN_AFTER = "口座変更申請_変更後_ゆうちょ口座記号"; | |||
| const FIELD_ACCOUNT_YUCHO_NO_AFTER = "口座変更申請_変更後_ゆうちょ口座番号"; | |||
| const FIELD_APPLICATION_CUSTOMER_CODE = "口座変更申請_顧客コード"; | |||
| const FIELD_SMBC_APPLICATION_DATETIME = "口座変更申請_SMBC申請日時"; | |||
| const FIELD_SMBC_ACCEPT_NO = "口座変更申請_SMBC受付番号"; | |||
| const FIELD_SMBC_RESULT = "口座変更申請_SMBC結果"; | |||
| protected const FIELDS = [ | |||
| ...parent::FIELDS, | |||
| self::FIELD_BANK_BRANCH_ID_BEFORE => FieldType::SINGLE_LINE_TEXT, | |||
| self::FIELD_BANK_CODE_BEFORE => FieldType::SINGLE_LINE_TEXT, | |||
| self::FIELD_BANK_NAME_BEFORE => FieldType::SINGLE_LINE_TEXT, | |||
| self::FIELD_BRANCH_CODE_BEFORE => FieldType::SINGLE_LINE_TEXT, | |||
| self::FIELD_BRANCH_NAME_BEFORE => FieldType::SINGLE_LINE_TEXT, | |||
| self::FIELD_ACCOUNT_TYPE_BEFORE => FieldType::NUMBER, | |||
| self::FIELD_ACCOUNT_NAME_KANA_BEFORE => FieldType::SINGLE_LINE_TEXT, | |||
| self::FIELD_ACCOUNT_NO_BEFORE => FieldType::NUMBER, | |||
| self::FIELD_ACCOUNT_YUCHO_SIGN_BEFORE => FieldType::NUMBER, | |||
| self::FIELD_ACCOUNT_YUCHO_NO_BEFORE => FieldType::NUMBER, | |||
| self::FIELD_BANK_BRANCH_ID_AFTER => FieldType::SINGLE_LINE_TEXT, | |||
| self::FIELD_BANK_CODE_AFTER => FieldType::SINGLE_LINE_TEXT, | |||
| self::FIELD_BANK_NAME_AFTER => FieldType::SINGLE_LINE_TEXT, | |||
| self::FIELD_BRANCH_CODE_AFTER => FieldType::SINGLE_LINE_TEXT, | |||
| self::FIELD_BRANCH_NAME_AFTER => FieldType::SINGLE_LINE_TEXT, | |||
| self::FIELD_ACCOUNT_TYPE_AFTER => FieldType::NUMBER, | |||
| self::FIELD_ACCOUNT_NAME_KANA_AFTER => FieldType::SINGLE_LINE_TEXT, | |||
| self::FIELD_ACCOUNT_NO_AFTER => FieldType::NUMBER, | |||
| self::FIELD_ACCOUNT_YUCHO_SIGN_AFTER => FieldType::NUMBER, | |||
| self::FIELD_ACCOUNT_YUCHO_NO_AFTER => FieldType::NUMBER, | |||
| self::FIELD_APPLICATION_CUSTOMER_CODE => FieldType::SINGLE_LINE_TEXT, | |||
| self::FIELD_SMBC_APPLICATION_DATETIME => FieldType::DATETIME, | |||
| self::FIELD_SMBC_ACCEPT_NO => FieldType::SINGLE_LINE_TEXT, | |||
| self::FIELD_SMBC_RESULT => FieldType::SINGLE_LINE_TEXT, | |||
| ]; | |||
| protected const FIELD_NAMES = [ | |||
| ...parent::FIELD_NAMES, | |||
| ]; | |||
| } | |||
| @@ -13,6 +13,7 @@ use Illuminate\Support\Facades\Auth; | |||
| * @property string phoneNumber | |||
| * @property string zipCode | |||
| * @property string address | |||
| * @property string bankBranchId | |||
| */ | |||
| class Customer extends KintoneModel | |||
| { | |||
| @@ -25,6 +26,7 @@ class Customer extends KintoneModel | |||
| const FIELD_PHONE_NUMBER = "電話番号"; | |||
| const FIELD_ZIP_CODE = "契約者_郵便番号"; | |||
| const FIELD_ADDRESS = "住所"; | |||
| const FIELD_BANK_BRANCH_ID = "ChargedBankBranchCode"; | |||
| protected const FIELDS = [ | |||
| ...parent::FIELDS, | |||
| @@ -35,6 +37,7 @@ class Customer extends KintoneModel | |||
| self::FIELD_PHONE_NUMBER => FieldType::LINK, | |||
| self::FIELD_ZIP_CODE => FieldType::SINGLE_LINE_TEXT, | |||
| self::FIELD_ADDRESS => FieldType::SINGLE_LINE_TEXT, | |||
| self::FIELD_BANK_BRANCH_ID => FieldType::SINGLE_LINE_TEXT, | |||
| ]; | |||
| protected const FIELD_NAMES = [ | |||
| @@ -47,6 +47,7 @@ abstract class GeneralApplication extends KintoneModel | |||
| protected const RELATIONS = [ | |||
| SeasonTicketContract::class, | |||
| Customer::class, | |||
| Bank::class, | |||
| ]; | |||
| public static function findByApplicationNo(string $applicationNo): static | |||
| @@ -3,6 +3,7 @@ | |||
| namespace App\Logic; | |||
| use App\Exceptions\AppCommonException; | |||
| use App\Kintone\Models\BankAccountUpdateApplication; | |||
| use App\Kintone\Models\Customer; | |||
| use App\Kintone\Models\GeneralApplication; | |||
| use App\Kintone\Models\Parking; | |||
| @@ -61,6 +62,10 @@ class GeneralApplicationManager | |||
| $this->setType("振替頻度変更"); | |||
| return; | |||
| } | |||
| if ($model instanceof BankAccountUpdateApplication) { | |||
| $this->setType("口座変更申請"); | |||
| return; | |||
| } | |||
| } | |||
| public function setCustomer(Customer $customer): static | |||
| @@ -102,6 +107,10 @@ class GeneralApplicationManager | |||
| $this->model->parkingName = $this->parking->parkingName; | |||
| } | |||
| if ($this->model instanceof BankAccountUpdateApplication) { | |||
| $this->model->applicationCustomerCode = $this->customer->customerCode; | |||
| } | |||
| return $this->model; | |||
| } | |||
| @@ -0,0 +1,22 @@ | |||
| <?php | |||
| namespace App\Models; | |||
| class SmbcPollStatus extends AppModel | |||
| { | |||
| const COL_NAME_CONDITION_DATETIME_TO = 'condition_datetime_to'; | |||
| protected $casts = [ | |||
| self::COL_NAME_CONDITION_DATETIME_TO => 'datetime', | |||
| ]; | |||
| public function getHistory(): ?HistoryModel | |||
| { | |||
| return null; | |||
| } | |||
| public function getModelName(): string | |||
| { | |||
| return "SMBC口座振替登録依頼確認ステータス"; | |||
| } | |||
| } | |||
| @@ -0,0 +1,16 @@ | |||
| <?php | |||
| namespace App\Util; | |||
| class EncodingUtil | |||
| { | |||
| public static function toUtf8FromSjis(string $source) | |||
| { | |||
| return mb_convert_encoding($source, "UTF8", 'SJIS'); | |||
| } | |||
| public static function toSjisFromUtf8(string $source) | |||
| { | |||
| return mb_convert_encoding($source, "SJIS", 'UTF8'); | |||
| } | |||
| } | |||
| @@ -27,6 +27,7 @@ return [ | |||
| 'applications' => [ | |||
| ...App\Kintone\Models\Customer::setConfig(), | |||
| ...App\Kintone\Models\Bank::setConfig(), | |||
| ...App\Kintone\Models\Parking::setConfig(), | |||
| ...App\Kintone\Models\SeasonTicketContract::setConfig(), | |||
| ...App\Kintone\Models\SeasonTicketContractEntry::setConfig(), | |||
| @@ -0,0 +1,20 @@ | |||
| <?php | |||
| return [ | |||
| /* | |||
| |-------------------------------------------------------------------------- | |||
| | SMBCのパスワード管理 | |||
| |-------------------------------------------------------------------------- | |||
| | | |||
| | キントーンAPIのホストを定義 | |||
| */ | |||
| 'systemPassword' => env("SMBC_SYSTEM_PASSWORD"), | |||
| 'searchPassword' => env("SMBC_SEARCH_PASSWORD"), | |||
| 'registerUrl' => env("SMBC_URL_REGISTER"), | |||
| 'searchUrl' => env("SMBC_URL_SEARCH"), | |||
| ]; | |||
| @@ -0,0 +1,30 @@ | |||
| <?php | |||
| use App\Util\MigrationHelper; | |||
| use Illuminate\Database\Migrations\Migration; | |||
| use Illuminate\Database\Schema\Blueprint; | |||
| use Illuminate\Support\Facades\Schema; | |||
| return new class extends Migration | |||
| { | |||
| /** | |||
| * Run the migrations. | |||
| */ | |||
| public function up(): void | |||
| { | |||
| Schema::create('smbc_poll_statuses', function (Blueprint $table) { | |||
| $helper = new MigrationHelper($table); | |||
| $helper->baseColumn(); | |||
| $table->dateTime('condition_datetime_to')->comment('前回検索条件_TO時刻'); | |||
| }); | |||
| } | |||
| /** | |||
| * Reverse the migrations. | |||
| */ | |||
| public function down(): void | |||
| { | |||
| Schema::dropIfExists('smbc_poll_statuses'); | |||
| } | |||
| }; | |||
| @@ -37,6 +37,7 @@ RouteHelper::post('/ask', App\Http\Controllers\Web\FAQ\AskController::class); | |||
| RouteHelper::post('/email/change/start', App\Http\Controllers\Web\Customer\ChangeEmailStartController::class); | |||
| RouteHelper::post('/email/change/verify', App\Http\Controllers\Web\Customer\ChangeEmailVerifyController::class); | |||
| RouteHelper::post('/customer/update-info-order', App\Http\Controllers\Web\Customer\UpdateUserInfoOrderController::class); | |||
| RouteHelper::get('/customer/bank-account-register/start', App\Http\Controllers\Web\Customer\BankAccountRegisterStartController::class); | |||
| RouteHelper::post('/password/setting/start', App\Http\Controllers\Web\Auth\PasswordSettingStartController::class); | |||
| RouteHelper::post('/password/setting/verify', App\Http\Controllers\Web\Auth\PasswordSettingVerifyController::class); | |||
| @@ -0,0 +1,21 @@ | |||
| <?php | |||
| namespace Tests\Feature\Http\API\SMBC; | |||
| use App\Http\API\SMBC\SMBC; | |||
| use App\Util\DateUtil; | |||
| use Tests\TestCase; | |||
| class SMBCTest extends TestCase | |||
| { | |||
| public function test_poll(): void | |||
| { | |||
| $from = DateUtil::now()->setDate(2023, 10, 5); | |||
| $to = DateUtil::now()->setDate(2023, 10, 7); | |||
| $result = SMBC::poll($from, $to); | |||
| $this->assertEquals("", $result->getMessage()); | |||
| $this->assertTrue($result->ok()); | |||
| } | |||
| } | |||
| @@ -0,0 +1,41 @@ | |||
| <?php | |||
| namespace Tests\Feature\Kintone; | |||
| use App\Kintone\Models\BankAccountUpdateApplication; | |||
| use App\Kintone\Models\Customer; | |||
| use App\Logic\GeneralApplicationManager; | |||
| use Tests\TestCase; | |||
| class BankAccountUpdateApplicationTest extends TestCase | |||
| { | |||
| public function test_simple(): void | |||
| { | |||
| $customer = Customer::findByCustomerCode("171"); | |||
| $application = new BankAccountUpdateApplication(); | |||
| $manager = new GeneralApplicationManager($application); | |||
| $manager | |||
| ->setCustomer($customer) | |||
| ->makeApplication(); | |||
| $application->bankBranchIdBefore = $customer->bankBranchId; | |||
| $application->bankBranchIdAfter = "1004"; | |||
| $application->bankCodeAfter = "1"; | |||
| $application->bankNameAfter = "みずほー"; | |||
| $application->branchCodeAfter = "4"; | |||
| $application->branchNameAfter = "まるのうち"; | |||
| $application->accountTypeAfter = "1"; | |||
| $application->accountNameKanaAfter = "テストタロウ"; | |||
| $application->accountNoAfter = "1234567"; | |||
| $application->accountYuchoSignAfter = "01234"; | |||
| $application->accountYuchoNoAfter = "12345678"; | |||
| $application->save(); | |||
| $this->assertTrue(true); | |||
| } | |||
| } | |||