| @@ -4,6 +4,7 @@ namespace App\Codes; | |||||
| enum EnvironmentName: string | enum EnvironmentName: string | ||||
| { | { | ||||
| case TEST = 'testing'; | |||||
| case LOCAL = 'local'; | case LOCAL = 'local'; | ||||
| case STAGING = 'staging'; | case STAGING = 'staging'; | ||||
| case PRODUCTOIN = 'production'; | 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 | protected function schedule(Schedule $schedule): void | ||||
| { | { | ||||
| Schedules\HeartBeat::register($schedule); | 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; | namespace App\Http\Controllers\Web; | ||||
| use App\Codes\EnvironmentName; | |||||
| use App\Codes\HTTPResultCode as ResultCode; | use App\Codes\HTTPResultCode as ResultCode; | ||||
| use App\Codes\UserRole; | use App\Codes\UserRole; | ||||
| use App\Exceptions\AppCommonException; | use App\Exceptions\AppCommonException; | ||||
| @@ -301,7 +302,13 @@ abstract class WebController extends BaseController | |||||
| ->json($ret) | ->json($ret) | ||||
| ->withHeaders($this->makeHeader()); | ->withHeaders($this->makeHeader()); | ||||
| } else { | } 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 .= " "; | ||||
| } | } | ||||
| $ret .= $this->order; | $ret .= $this->order; | ||||
| logger(sprintf("QUERY[%s]:%s", $this->appName, $ret)); | |||||
| // logger(sprintf("QUERY[%s]:%s", $this->appName, $ret)); | |||||
| return $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 phoneNumber | ||||
| * @property string zipCode | * @property string zipCode | ||||
| * @property string address | * @property string address | ||||
| * @property string bankBranchId | |||||
| */ | */ | ||||
| class Customer extends KintoneModel | class Customer extends KintoneModel | ||||
| { | { | ||||
| @@ -25,6 +26,7 @@ class Customer extends KintoneModel | |||||
| const FIELD_PHONE_NUMBER = "電話番号"; | const FIELD_PHONE_NUMBER = "電話番号"; | ||||
| const FIELD_ZIP_CODE = "契約者_郵便番号"; | const FIELD_ZIP_CODE = "契約者_郵便番号"; | ||||
| const FIELD_ADDRESS = "住所"; | const FIELD_ADDRESS = "住所"; | ||||
| const FIELD_BANK_BRANCH_ID = "ChargedBankBranchCode"; | |||||
| protected const FIELDS = [ | protected const FIELDS = [ | ||||
| ...parent::FIELDS, | ...parent::FIELDS, | ||||
| @@ -35,6 +37,7 @@ class Customer extends KintoneModel | |||||
| self::FIELD_PHONE_NUMBER => FieldType::LINK, | self::FIELD_PHONE_NUMBER => FieldType::LINK, | ||||
| self::FIELD_ZIP_CODE => FieldType::SINGLE_LINE_TEXT, | self::FIELD_ZIP_CODE => FieldType::SINGLE_LINE_TEXT, | ||||
| self::FIELD_ADDRESS => FieldType::SINGLE_LINE_TEXT, | self::FIELD_ADDRESS => FieldType::SINGLE_LINE_TEXT, | ||||
| self::FIELD_BANK_BRANCH_ID => FieldType::SINGLE_LINE_TEXT, | |||||
| ]; | ]; | ||||
| protected const FIELD_NAMES = [ | protected const FIELD_NAMES = [ | ||||
| @@ -47,6 +47,7 @@ abstract class GeneralApplication extends KintoneModel | |||||
| protected const RELATIONS = [ | protected const RELATIONS = [ | ||||
| SeasonTicketContract::class, | SeasonTicketContract::class, | ||||
| Customer::class, | Customer::class, | ||||
| Bank::class, | |||||
| ]; | ]; | ||||
| public static function findByApplicationNo(string $applicationNo): static | public static function findByApplicationNo(string $applicationNo): static | ||||
| @@ -3,6 +3,7 @@ | |||||
| namespace App\Logic; | namespace App\Logic; | ||||
| use App\Exceptions\AppCommonException; | use App\Exceptions\AppCommonException; | ||||
| use App\Kintone\Models\BankAccountUpdateApplication; | |||||
| use App\Kintone\Models\Customer; | use App\Kintone\Models\Customer; | ||||
| use App\Kintone\Models\GeneralApplication; | use App\Kintone\Models\GeneralApplication; | ||||
| use App\Kintone\Models\Parking; | use App\Kintone\Models\Parking; | ||||
| @@ -61,6 +62,10 @@ class GeneralApplicationManager | |||||
| $this->setType("振替頻度変更"); | $this->setType("振替頻度変更"); | ||||
| return; | return; | ||||
| } | } | ||||
| if ($model instanceof BankAccountUpdateApplication) { | |||||
| $this->setType("口座変更申請"); | |||||
| return; | |||||
| } | |||||
| } | } | ||||
| public function setCustomer(Customer $customer): static | public function setCustomer(Customer $customer): static | ||||
| @@ -102,6 +107,10 @@ class GeneralApplicationManager | |||||
| $this->model->parkingName = $this->parking->parkingName; | $this->model->parkingName = $this->parking->parkingName; | ||||
| } | } | ||||
| if ($this->model instanceof BankAccountUpdateApplication) { | |||||
| $this->model->applicationCustomerCode = $this->customer->customerCode; | |||||
| } | |||||
| return $this->model; | 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' => [ | 'applications' => [ | ||||
| ...App\Kintone\Models\Customer::setConfig(), | ...App\Kintone\Models\Customer::setConfig(), | ||||
| ...App\Kintone\Models\Bank::setConfig(), | |||||
| ...App\Kintone\Models\Parking::setConfig(), | ...App\Kintone\Models\Parking::setConfig(), | ||||
| ...App\Kintone\Models\SeasonTicketContract::setConfig(), | ...App\Kintone\Models\SeasonTicketContract::setConfig(), | ||||
| ...App\Kintone\Models\SeasonTicketContractEntry::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/start', App\Http\Controllers\Web\Customer\ChangeEmailStartController::class); | ||||
| RouteHelper::post('/email/change/verify', App\Http\Controllers\Web\Customer\ChangeEmailVerifyController::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::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/start', App\Http\Controllers\Web\Auth\PasswordSettingStartController::class); | ||||
| RouteHelper::post('/password/setting/verify', App\Http\Controllers\Web\Auth\PasswordSettingVerifyController::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); | |||||
| } | |||||
| } | |||||