aonestar
.public
Tables
(current)
Columns
Constraints
Relationships
Orphan Tables
Anomalies
Routines
sp_gen_door_credential
Parameters
Name
Type
Mode
i_register_id
integer
IN
i_user
text
IN (DEFAULT NULL)
Definition
DECLARE v_pin_on boolean := is_interface_active('DOORLOCK_PIN'); v_qr_on boolean := is_interface_active('DOORLOCK_QR'); v_url text := NULLIF(BTRIM(fn_intf_param('DOORLOCK_PIN', 'api_url', '')), ''); v_access_token text := NULLIF(BTRIM(fn_intf_param('DOORLOCK_PIN', 'api_key', '')), ''); v_room_id int; v_room_number text; v_booking_id int; v_valid_from timestamp; v_valid_until timestamp; v_actual_from timestamp; v_actual_until timestamp; v_payload jsonb; v_result json; v_resp jsonb; v_pin text; v_credential_ref jsonb; v_extra jsonb; v_qr jsonb; v_new_id int; v_reason text; v_state text; v_message text; v_detail text; v_hint text; v_context text; BEGIN IF NOT v_pin_on AND NOT v_qr_on THEN RETURN NULL; END IF; SELECT rg.room_id, rg.booking_id INTO v_room_id, v_booking_id FROM registration rg WHERE rg.id = i_register_id; IF NOT FOUND THEN RAISE EXCEPTION 'Registration not found: %', i_register_id; END IF; SELECT f.valid_from, f.valid_until INTO v_valid_from, v_valid_until FROM fn_doorlock_validity(i_register_id) AS f; IF v_pin_on THEN -- fn_doorlock_roomno คืน '101' เมื่อ demo mode (DEMO_MODE_DOORLOCK_PIN ที่ authen) v_room_number := fn_doorlock_roomno(v_room_id); IF NULLIF(v_room_number, '') IS NULL THEN RAISE EXCEPTION 'Room number is missing for registration %', i_register_id; END IF; IF v_url IS NULL THEN RAISE EXCEPTION 'DOORLOCK_PIN api_url is not configured'; END IF; IF v_access_token IS NULL THEN RAISE EXCEPTION 'DOORLOCK_PIN api_key is not configured'; END IF; -- addpasscode รับรูปแบบ ISO ได้ (พิสูจน์แล้วบน dev) จึงคงไว้ตามเดิม -- ต่างจาก staychange/getpasscodes ที่รับเฉพาะ M/D/YYYY v_payload := jsonb_build_object( 'hotel_code', fn_prop_code(), 'roomno', v_room_number, 'effect', to_char(v_valid_from, 'YYYY-MM-DD HH24:MI:SS'), 'expire', to_char(v_valid_until, 'YYYY-MM-DD HH24:MI:SS') ); PERFORM http_set_curlopt('CURLOPT_TIMEOUT', '10'); -- sp_call_rpc: ใส่ bearer header ให้, คืน fn_result_error('99',...) เมื่อ HTTP ไม่ 2xx, -- และ raise เองเมื่อ transport ล้ม/body ไม่ใช่ JSON -> ทั้งสองทางไหลลง WHEN OTHERS ข้างล่าง v_result := sp_call_rpc(v_url, v_access_token, v_payload, 'DoorLock'); IF NOT is_result_success(v_result) THEN RAISE EXCEPTION '%', COALESCE(NULLIF(v_result->>'result_msg', ''), 'DoorLock API call failed'); END IF; v_resp := v_result::jsonb; IF COALESCE(v_resp->>'result_code', '') <> '0' THEN RAISE EXCEPTION '%', COALESCE(NULLIF(v_resp->>'result_msg', ''), 'DoorLock API returned result_code ' || COALESCE(v_resp->>'result_code', 'NULL')); END IF; v_pin := NULLIF(v_resp #>> '{result_data,keyboardPwd}', ''); v_credential_ref := v_resp #> '{result_data,keyboardPwdId}'; IF v_pin IS NULL THEN RAISE EXCEPTION 'DoorLock API success response has no keyboardPwd'; END IF; -- vendor API v1.5: ประตูส่วนกลาง / ประตูห้องเชื่อม ที่ passcode ใบนี้เปิดได้ -- vendor ส่ง [] มาเมื่อไม่มี -> เก็บเป็น NULL ไม่เก็บ array ว่าง v_extra := jsonb_strip_nulls(jsonb_build_object( 'public_door', NULLIF(v_resp #> '{result_data,public_door}', '[]'::jsonb), 'sub_door', NULLIF(v_resp #> '{result_data,sub_door}', '[]'::jsonb))); v_extra := NULLIF(v_extra, '{}'::jsonb); v_actual_from := COALESCE(NULLIF(v_resp->>'adjusted_effect', '')::timestamp, v_valid_from); v_actual_until := COALESCE(NULLIF(v_resp->>'adjusted_expire', '')::timestamp, v_valid_until); INSERT INTO intf_doorlock_logs ( register_id, room_id, booking_id, credential_type, pin_code, credential_ref, extra_doors, valid_from, valid_until, status, api_request, api_response, log_user ) VALUES ( i_register_id, v_room_id, v_booking_id, 'PIN', v_pin, v_resp #>> '{result_data,keyboardPwdId}', v_extra, v_actual_from, v_actual_until, 'P', v_payload, v_resp, i_user ) RETURNING id INTO v_new_id; -- V20260807.10: ถ้าเรียกด้วย clearoldpasscode = true vendor จะล้าง passcode เดิม -- ของ "ห้องนี้ในช่วงเวลาที่ทับกัน" ให้ (ค่า default ฝั่ง vendor คือ false) -- ใบเก่าที่ยัง 'P' อยู่ถือว่าถูกแทนที่แล้ว ต้อง mark ตาม -- scope เป็นห้อง -- ไม่ใช่ registration เพราะ vendor ผูก passcode กับตัวล็อกของห้อง UPDATE intf_doorlock_logs SET status = 'R', revoke_time = now(), revoke_reason = 'REISSUE' WHERE room_id = v_room_id AND credential_type = 'PIN' AND status = 'P' AND id <> v_new_id AND tsrange(valid_from, valid_until) && tsrange(v_actual_from, v_actual_until); END IF; -- QR: ไม่แตะ vendor API, fail-soft อยู่ข้างใน -> คืน NULL เมื่อปิด interface IF v_qr_on THEN v_qr := sp_gen_door_qr(i_register_id, i_user); END IF; IF v_pin IS NULL AND v_qr IS NULL THEN RETURN NULL; END IF; RETURN jsonb_strip_nulls(jsonb_build_object( 'pin_code', v_pin, 'credential_ref', v_credential_ref, 'valid_from', COALESCE(v_actual_from, (v_qr->>'valid_from')::timestamp), 'valid_until', COALESCE(v_actual_until, (v_qr->>'valid_until')::timestamp), 'qr_payload', v_qr->>'qr_payload', 'qr_url', v_qr->>'qr_url' ) || COALESCE(v_extra, '{}'::jsonb)); EXCEPTION WHEN SQLSTATE '99999' THEN RAISE; WHEN OTHERS THEN GET STACKED DIAGNOSTICS v_state = RETURNED_SQLSTATE, v_message = MESSAGE_TEXT, v_detail = PG_EXCEPTION_DETAIL, v_hint = PG_EXCEPTION_HINT, v_context = PG_EXCEPTION_CONTEXT; v_reason := COALESCE(NULLIF(v_message, ''), 'Unknown DoorLock error'); -- ต้อง raise ไม่ใช่ return: caller ต้อง rollback check-in ทั้งชุดเมื่อออก PIN ไม่ได้ PERFORM sp_raise_error(jsonb_build_object( 'result_code', '4', 'msg_code', '20216', 'result_msg', sys_msg_format('20216', 'Unable to create door passcode: %s', v_reason), 'result_data', jsonb_build_object( 'source', 'DOORLOCK_PIN', 'register_id', i_register_id, 'room_id', v_room_id, 'booking_id', v_booking_id, 'valid_from', v_valid_from, 'valid_until', v_valid_until, 'api_request', v_payload, 'api_response', v_resp, 'upstream_error', jsonb_build_object( 'sql_state', v_state, 'message', v_message, 'detail', v_detail, 'hint', v_hint, 'context', v_context ) ) )::json); RETURN NULL; END