| @@ -37,7 +37,7 @@ class LoginController extends WebController | |||
| $access = Customer::getAccess(); | |||
| $query = Customer::getQuery()->where(Customer::FIELD_EMAIL, $param->email); | |||
| $customer = $access->all($query); | |||
| $customer = $access->some($query); | |||
| if ($customer->count() !== 1) { | |||
| return $this->failedResponse(); | |||
| @@ -55,10 +55,18 @@ class LoginController extends WebController | |||
| if ($user instanceof User) { | |||
| // すでにユーザー登録されているケース | |||
| //データ同期 | |||
| if ($user->email !== $param->email) { | |||
| $user->email = $param->email; | |||
| $user->save(); | |||
| } | |||
| if ($user->kintone_customer_code !== $customer->getNumber(Customer::FIELD_CUSTOMER_CODE)) { | |||
| $user->kintone_customer_code = $customer->getNumber(Customer::FIELD_CUSTOMER_CODE); | |||
| $user->save(); | |||
| } | |||
| if (Auth::attempt([ | |||
| 'email' => $param->email, | |||
| 'password' => $param->password, | |||
| @@ -74,6 +82,7 @@ class LoginController extends WebController | |||
| $user->email = $param->email; | |||
| $user->kintone_id = $customer->getRecordId(); | |||
| $user->password = $password; | |||
| $user->kintone_customer_code = $customer->getNumber(Customer::FIELD_CUSTOMER_CODE); | |||
| $user->save(); | |||
| if (Auth::attempt([ | |||
| 'email' => $param->email, | |||
| @@ -0,0 +1,43 @@ | |||
| <?php | |||
| namespace App\Http\Controllers\Web\SeasonTicketContract; | |||
| use App\Http\Controllers\Web\WebController; | |||
| use App\Kintone\Repositories\SeasonTicketContractRepository; | |||
| use Illuminate\Http\JsonResponse; | |||
| use Illuminate\Http\Request; | |||
| use Illuminate\Support\Facades\Auth; | |||
| class SeasonTicketContractsController extends WebController | |||
| { | |||
| public function name(): string | |||
| { | |||
| return "定期契約情報一覧取得"; | |||
| } | |||
| public function description(): string | |||
| { | |||
| return "定期契約情報の一覧を取得する"; | |||
| } | |||
| public function __construct(protected SeasonTicketContractsParam $param) | |||
| { | |||
| parent::__construct(); | |||
| $this->middleware('auth:sanctum'); | |||
| } | |||
| protected function run(Request $request): JsonResponse | |||
| { | |||
| $user = Auth::user(); | |||
| $list = SeasonTicketContractRepository::get($user->kintone_customer_code); | |||
| foreach ($list as $ele) { | |||
| $result[] = $ele->toArray(); | |||
| } | |||
| return $this->successResponse($result); | |||
| } | |||
| } | |||
| @@ -0,0 +1,15 @@ | |||
| <?php | |||
| namespace App\Http\Controllers\Web\SeasonTicketContract; | |||
| use App\Http\Controllers\Web\BaseParam; | |||
| /** | |||
| */ | |||
| class SeasonTicketContractsParam extends BaseParam | |||
| { | |||
| public function rules(): array | |||
| { | |||
| return []; | |||
| } | |||
| } | |||
| @@ -35,6 +35,7 @@ class KintoneAccess | |||
| private bool $hasNext = false; | |||
| private const URL_RECORD = "/k/v1/record.json"; | |||
| private const URL_RECORDS = "/k/v1/records.json"; | |||
| private const URL_CURSOL = "/k/v1/records/cursor.json"; | |||
| private const URL_FILE = "/k/v1/file.json"; | |||
| @@ -109,6 +110,48 @@ class KintoneAccess | |||
| return $result; | |||
| } | |||
| /** | |||
| * @param KintoneRecordQuery|null|null $query | |||
| * @return Collection<string,TValue> | |||
| */ | |||
| public function some(KintoneRecordQuery|null $query = null) | |||
| { | |||
| $data = [ | |||
| "app" => $this->appId, | |||
| 'fields' => $this->fields, | |||
| ]; | |||
| if ($query !== null) { | |||
| $data["query"] = $query->toQuery(); | |||
| } | |||
| $response = Http::withHeaders([ | |||
| "X-Cybozu-API-Token" => $this->apiToken, | |||
| ])->get($this->getRecordsUrl(), $data); | |||
| if ($response->failed()) { | |||
| $e = $response->toException(); | |||
| if ($e instanceof Exception) { | |||
| Log::error($e->getMessage()); | |||
| throw $e; | |||
| } | |||
| } | |||
| $ret = collect(); | |||
| foreach ($response["records"] as $data) { | |||
| /** | |||
| * @var KintoneModel $model | |||
| */ | |||
| $model = new $this->appName(); | |||
| $model->setDataFromRecordResponse($data); | |||
| $ret->put($model->getRecordId(), $model); | |||
| } | |||
| return $ret; | |||
| } | |||
| /** | |||
| * @param TValue $model | |||
| * @return void | |||
| @@ -340,6 +383,10 @@ class KintoneAccess | |||
| { | |||
| return $this->getUrl(self::URL_RECORD); | |||
| } | |||
| private function getRecordsUrl() | |||
| { | |||
| return $this->getUrl(self::URL_RECORDS); | |||
| } | |||
| private function getCursorUrl() | |||
| { | |||
| @@ -2,15 +2,20 @@ | |||
| namespace App\Kintone\Models; | |||
| /** | |||
| * アプリ名 顧客マスタ | |||
| */ | |||
| class Customer extends KintoneModel | |||
| { | |||
| const CONFIG_KEY = "KINTONE_APP_CUSTOMER"; | |||
| const FIELD_CUSTOMER_CODE = "CustomerCode"; | |||
| const FIELD_CUSTOMER_NAME = "CustomerName"; | |||
| const FIELD_EMAIL = "メールアドレス"; | |||
| protected const FIELDS = [ | |||
| ...parent::FIELDS, | |||
| self::FIELD_CUSTOMER_CODE => FieldType::NUMBER, | |||
| self::FIELD_CUSTOMER_NAME => FieldType::SINGLE_LINE_TEXT, | |||
| self::FIELD_EMAIL => FieldType::LINK, | |||
| ]; | |||
| @@ -20,9 +25,4 @@ class Customer extends KintoneModel | |||
| self::FIELD_CUSTOMER_NAME => 'customer_name', | |||
| self::FIELD_EMAIL => 'email', | |||
| ]; | |||
| protected function setDataCustom(array $data): bool | |||
| { | |||
| return true; | |||
| } | |||
| } | |||
| @@ -162,7 +162,7 @@ abstract class KintoneModel | |||
| $type = FieldType::tryFrom($type); | |||
| if ($type === null) continue; | |||
| if ($type === FieldType::DATETIME) { | |||
| if (in_array($type, [FieldType::DATETIME, FieldType::DATE])) { | |||
| if ($value) { | |||
| data_set($this->data, $fieldCode, DateUtil::parse($value)); | |||
| } else { | |||
| @@ -302,7 +302,10 @@ abstract class KintoneModel | |||
| $type = data_get(static::FIELDS, $fieldCode); | |||
| $columnName = data_get(static::FIELD_NAMES, $fieldCode, $fieldCode); | |||
| $columnName = data_get(static::FIELD_NAMES, $fieldCode, null); | |||
| if ($columnName === null) { | |||
| continue; | |||
| } | |||
| if ($type === null) { | |||
| $ret[$columnName] = $value; | |||
| @@ -317,6 +320,14 @@ abstract class KintoneModel | |||
| } | |||
| continue; | |||
| } | |||
| if ($type === FieldType::DATE) { | |||
| if ($value instanceof Carbon) { | |||
| $ret[$columnName] = $value->format('Y/m/d'); | |||
| } else { | |||
| $ret[$columnName] = $value; | |||
| } | |||
| continue; | |||
| } | |||
| $ret[$columnName] = $value; | |||
| @@ -0,0 +1,23 @@ | |||
| <?php | |||
| namespace App\Kintone\Models; | |||
| /** | |||
| * アプリ名 駐車場マスタ | |||
| */ | |||
| class Paking extends KintoneModel | |||
| { | |||
| const CONFIG_KEY = "KINTONE_APP_PARKING"; | |||
| const FIELD_PARKING_NAME = "駐車場名"; | |||
| protected const FIELDS = [ | |||
| ...parent::FIELDS, | |||
| self::FIELD_PARKING_NAME => FieldType::SINGLE_LINE_TEXT, | |||
| ]; | |||
| protected const FIELD_NAMES = [ | |||
| ...parent::FIELD_NAMES, | |||
| self::FIELD_PARKING_NAME => 'parking_name', | |||
| ]; | |||
| } | |||
| @@ -0,0 +1,29 @@ | |||
| <?php | |||
| namespace App\Kintone\Models; | |||
| /** | |||
| * アプリ名 車室情報2 | |||
| */ | |||
| class ParkingRoom extends KintoneModel | |||
| { | |||
| const CONFIG_KEY = "KINTONE_APP_PARKING_ROOM"; | |||
| const FIELD_PARKING_NAME = "定期駐車場"; | |||
| const FIELD_ROOM_NO = "車室番号"; | |||
| const FIELD_SEASON_TICKET_CONTRACT_RECORD_NO = "契約情報"; | |||
| protected const FIELDS = [ | |||
| ...parent::FIELDS, | |||
| self::FIELD_PARKING_NAME => FieldType::SINGLE_LINE_TEXT, | |||
| self::FIELD_ROOM_NO => FieldType::SINGLE_LINE_TEXT, | |||
| self::FIELD_SEASON_TICKET_CONTRACT_RECORD_NO => FieldType::NUMBER, | |||
| ]; | |||
| protected const FIELD_NAMES = [ | |||
| ...parent::FIELD_NAMES, | |||
| self::FIELD_PARKING_NAME => 'parking_name', | |||
| self::FIELD_ROOM_NO => 'room_no', | |||
| self::FIELD_SEASON_TICKET_CONTRACT_RECORD_NO => 'season_ticekt_contract_record_no', | |||
| ]; | |||
| } | |||
| @@ -2,24 +2,33 @@ | |||
| namespace App\Kintone\Models; | |||
| /** | |||
| * アプリ名 車室情報管理 | |||
| */ | |||
| class SeasonTicketContract extends KintoneModel | |||
| { | |||
| const CONFIG_KEY = "KINTONE_APP_SEASON_TICKET_CONTRACT"; | |||
| const FIELD_CUSTOMER_NAME = "顧客名"; | |||
| const FIELD_CUSTOMER_CODE = "顧客コード"; | |||
| const FIELD_SEASON_TICKET_SEQ_NO = "定期券番号_0"; | |||
| const FIELD_VEHICLE_NO = "車両番号"; | |||
| const FIELD_CONTRACT_START_DATE = "契約日"; | |||
| const FIELD_CONTRACT_END_DATE = "解約日"; | |||
| protected const FIELDS = [ | |||
| ...parent::FIELDS, | |||
| self::FIELD_CUSTOMER_NAME => FieldType::SINGLE_LINE_TEXT, | |||
| self::FIELD_CUSTOMER_CODE => FieldType::NUMBER, | |||
| self::FIELD_SEASON_TICKET_SEQ_NO => FieldType::SINGLE_LINE_TEXT, | |||
| self::FIELD_VEHICLE_NO => FieldType::SINGLE_LINE_TEXT, | |||
| self::FIELD_CONTRACT_START_DATE => FieldType::DATE, | |||
| self::FIELD_CONTRACT_END_DATE => FieldType::DATE, | |||
| ]; | |||
| protected const FIELD_NAMES = [ | |||
| ...parent::FIELD_NAMES, | |||
| self::FIELD_CUSTOMER_NAME => 'customer_name', | |||
| self::FIELD_SEASON_TICKET_SEQ_NO => 'season_ticket_seq_no', | |||
| self::FIELD_VEHICLE_NO => 'vehicle_no', | |||
| self::FIELD_CONTRACT_START_DATE => 'contract_start_date', | |||
| self::FIELD_CONTRACT_END_DATE => 'contract_end_date', | |||
| ]; | |||
| protected function setDataCustom(array $data): bool | |||
| { | |||
| return true; | |||
| } | |||
| } | |||
| @@ -0,0 +1,57 @@ | |||
| <?php | |||
| namespace App\Kintone\Repositories; | |||
| use App\Kintone\Models\ParkingRoom; | |||
| use App\Kintone\Models\SeasonTicketContract; | |||
| use Illuminate\Support\Collection; | |||
| class SeasonTicketContractRepository | |||
| { | |||
| /** | |||
| * キーは車室情報管理のレコード番号 | |||
| * @param string $customerCode | |||
| * @return Collection<string, SeasonTicketContractRepositoryData> | |||
| */ | |||
| static function get(string $customerCode): Collection | |||
| { | |||
| $ret = collect(); | |||
| $query = SeasonTicketContract::getQuery() | |||
| ->where(SeasonTicketContract::FIELD_CUSTOMER_CODE, $customerCode); | |||
| $seasonTicketContracts = SeasonTicketContract::getAccess() | |||
| ->some($query); | |||
| $recordIds = []; | |||
| foreach ($seasonTicketContracts as $seasonTicketContract) { | |||
| $recordIds[] = $seasonTicketContract->getRecordId(); | |||
| } | |||
| $query = ParkingRoom::getQuery() | |||
| ->whereIn(ParkingRoom::FIELD_SEASON_TICKET_CONTRACT_RECORD_NO, $recordIds); | |||
| $parkings = ParkingRoom::getAccess() | |||
| ->some($query); | |||
| /** | |||
| * @var Collection<string, ParkingRoom> | |||
| */ | |||
| $parkingsBySeasonTicketContractRecordId = collect(); | |||
| foreach ($parkings as $parking) { | |||
| $parkingsBySeasonTicketContractRecordId->put($parking->getNumber(ParkingRoom::FIELD_SEASON_TICKET_CONTRACT_RECORD_NO), $parking); | |||
| } | |||
| /** | |||
| * @var SeasonTicketContract $seasonTicketContract | |||
| */ | |||
| foreach ($seasonTicketContracts as $seasonTicketContract) { | |||
| $parking = null; | |||
| $parking = $parkingsBySeasonTicketContractRecordId->get($seasonTicketContract->getRecordId(), null); | |||
| $ret->put($seasonTicketContract->getRecordId(), new SeasonTicketContractRepositoryData($seasonTicketContract, $parking)); | |||
| } | |||
| return $ret; | |||
| } | |||
| } | |||
| @@ -0,0 +1,23 @@ | |||
| <?php | |||
| namespace App\Kintone\Repositories; | |||
| use App\Kintone\Models\ParkingRoom; | |||
| use App\Kintone\Models\SeasonTicketContract; | |||
| class SeasonTicketContractRepositoryData | |||
| { | |||
| public function __construct( | |||
| public SeasonTicketContract $seasonTicketContract, | |||
| public ParkingRoom|null $parkingRoom = null, | |||
| ) { | |||
| } | |||
| public function toArray(): array | |||
| { | |||
| return [ | |||
| ...$this->parkingRoom !== null ? $this->parkingRoom->toArray() : [], | |||
| ...$this->seasonTicketContract->toArray(), | |||
| ]; | |||
| } | |||
| } | |||
| @@ -27,6 +27,7 @@ return [ | |||
| 'applications' => [ | |||
| ...App\Kintone\Models\Customer::setConfig(), | |||
| ...App\Kintone\Models\ParkingRoom::setConfig(), | |||
| ...App\Kintone\Models\SeasonTicketContract::setConfig(), | |||
| ], | |||
| @@ -18,9 +18,11 @@ return new class extends Migration | |||
| $table->string('email')->unique()->comment('Email'); | |||
| $table->string('password')->comment('ログインパスワード'); | |||
| $table->string('kintone_id')->comment('KintoneID'); | |||
| $table->string('kintone_customer_code')->comment('顧客コード'); | |||
| $helper->index(1, ['email']); | |||
| $helper->index(2, ['kintone_id']); | |||
| $helper->index(3, ['kintone_customer_code']); | |||
| }); | |||
| Schema::create('user_histories', function (Blueprint $table) { | |||
| $helper = new MigrationHelper($table, true); | |||
| @@ -29,9 +31,11 @@ return new class extends Migration | |||
| $table->string('email')->comment('Email'); | |||
| $table->string('password')->comment('ログインパスワード'); | |||
| $table->string('kintone_id')->comment('KintoneID'); | |||
| $table->string('kintone_customer_code')->comment('顧客コード'); | |||
| $helper->index(1, ['email']); | |||
| $helper->index(2, ['kintone_id']); | |||
| $helper->index(3, ['kintone_customer_code']); | |||
| }); | |||
| } | |||
| @@ -17,3 +17,5 @@ use App\Util\RouteHelper; | |||
| RouteHelper::post('/login', App\Http\Controllers\Web\Auth\LoginController::class); | |||
| RouteHelper::get('/logout', App\Http\Controllers\Web\Auth\LogoutController::class); | |||
| RouteHelper::get('/me', App\Http\Controllers\Web\Auth\MeController::class); | |||
| RouteHelper::get('/season-ticket-contracts', App\Http\Controllers\Web\SeasonTicketContract\SeasonTicketContractsController::class); | |||
| @@ -0,0 +1,16 @@ | |||
| <?php | |||
| namespace Tests\Feature\Kintone; | |||
| use App\Kintone\Repositories\SeasonTicketContractRepository; | |||
| use Tests\TestCase; | |||
| class SeasonTicketContractRepositoryTest extends TestCase | |||
| { | |||
| public function test_simple(): void | |||
| { | |||
| $list = SeasonTicketContractRepository::get("9362"); | |||
| $this->assertCount(1, $list); | |||
| } | |||
| } | |||
| @@ -4,68 +4,55 @@ namespace Tests\Feature; | |||
| use App\Kintone\Models\Customer; | |||
| use App\Kintone\Models\SeasonTicketContract; | |||
| use Illuminate\Support\Carbon; | |||
| use Tests\TestCase; | |||
| class KintoneAccessTest extends TestCase | |||
| { | |||
| public function test_find(): void | |||
| { | |||
| $access = SeasonTicketContract::getAccess(); | |||
| /** | |||
| * @var SeasonTicketContract | |||
| */ | |||
| $model = $access->find(505); | |||
| $this->assertEquals("塩山兼司", $model->getStr(SeasonTicketContract::FIELD_CUSTOMER_NAME)); | |||
| } | |||
| public function test_all(): void | |||
| { | |||
| $model = new SeasonTicketContract(); | |||
| $access = SeasonTicketContract::getAccess(); | |||
| $query = SeasonTicketContract::getQuery(); | |||
| $query->where(SeasonTicketContract::FIELD_CUSTOMER_NAME, "井出侑加"); | |||
| $ret = $access->all($query); | |||
| $access = Customer::getAccess(); | |||
| $query = Customer::getQuery(); | |||
| $query->where(Customer::FIELD_CUSTOMER_NAME, "岩渕奏亮(テスト)"); | |||
| $this->assertEquals(1, $ret->count()); | |||
| $list = $access->all($query); | |||
| $this->assertEquals(1, $list->count()); | |||
| /** | |||
| * @var SeasonTicketContract | |||
| */ | |||
| $model = $ret[0]; | |||
| $model = $list->firstOrFail(); | |||
| $this->assertEquals("井出侑加", $model->getStr(SeasonTicketContract::FIELD_CUSTOMER_NAME)); | |||
| $this->assertEquals("岩渕奏亮(テスト)", $model->getStr(Customer::FIELD_CUSTOMER_NAME)); | |||
| $this->assertEquals("test@aa.com", $model->getStr(Customer::FIELD_EMAIL)); | |||
| $array = $model->toArray(); | |||
| $this->assertEquals("井出侑加", $array['customer_name']); | |||
| $array = $model->toArray([Customer::FIELD_CUSTOMER_NAME, Customer::FIELD_EMAIL]); | |||
| $this->assertEquals("岩渕奏亮(テスト)", $array['customer_name']); | |||
| $this->assertEquals("test@aa.com", $array['email']); | |||
| } | |||
| public function test_customer(): void | |||
| public function test_some(): void | |||
| { | |||
| $access = Customer::getAccess(); | |||
| $query = Customer::getQuery(); | |||
| $query->where(Customer::FIELD_CUSTOMER_NAME, "山下千晶"); | |||
| $access = SeasonTicketContract::getAccess(); | |||
| $query = SeasonTicketContract::getQuery(); | |||
| $query->where(SeasonTicketContract::FIELD_CUSTOMER_CODE, "9362"); | |||
| $list = $access->all($query); | |||
| $list = $access->some($query); | |||
| $this->assertEquals(1, $list->count()); | |||
| /** | |||
| * @var Customer | |||
| */ | |||
| $model = $list[0]; | |||
| $model = $list->firstOrFail(); | |||
| $this->assertEquals("山下千晶", $model->getStr(Customer::FIELD_CUSTOMER_NAME)); | |||
| $this->assertEquals("shi.yy16@gmail.com", $model->getStr(Customer::FIELD_EMAIL)); | |||
| $this->assertEquals("", $model->getStr(SeasonTicketContract::FIELD_SEASON_TICKET_SEQ_NO)); | |||
| $this->assertEquals("交野市 て 1095", $model->getStr(SeasonTicketContract::FIELD_VEHICLE_NO)); | |||
| $this->assertEquals(new Carbon("2023-05-01"), $model->getDate(SeasonTicketContract::FIELD_CONTRACT_START_DATE)); | |||
| $this->assertEquals(new Carbon("2121-12-31"), $model->getDate(SeasonTicketContract::FIELD_CONTRACT_END_DATE)); | |||
| $array = $model->toArray([Customer::FIELD_CUSTOMER_NAME, Customer::FIELD_EMAIL]); | |||
| $this->assertEquals("山下千晶", $array['customer_name']); | |||
| $this->assertEquals("shi.yy16@gmail.com", $array['email']); | |||
| $array = $model->toArray(); | |||
| $this->assertEquals("", $array['season_ticket_seq_no']); | |||
| $this->assertEquals("交野市 て 1095", $array['vehicle_no']); | |||
| $this->assertEquals("2023/05/01", $array['contract_start_date']); | |||
| $this->assertEquals("2121/12/31", $array['contract_end_date']); | |||
| } | |||
| } | |||