aonestar
.public
Tables
(current)
Columns
Constraints
Relationships
Orphan Tables
Anomalies
Routines
sp_change_departure
Parameters
Name
Type
Mode
register_id
integer
IN
new_departure
date
IN
user_name
text
IN (DEFAULT NULL)
Definition
DECLARE _sqlstate TEXT; _detail TEXT; _hint TEXT; _context TEXT; _msg_text TEXT; _result_code TEXT; _msg_code TEXT; _arrival date; _status char(1); _room_id int; _rateplan_id int; _old_departure date; _sys date; _end_date date; _posted_date date; checkin_data jsonb; verify_result json; -- split folio (V20260821.10.4) _shared_register_id int; _share_rate boolean; _split_folio boolean; BEGIN CALL sp_check_endday_process(); user_name := coalesce(nullif(user_name, ''), fn_current_user()); CALL sp_check_record_lock(user_name, register_id := $1); SELECT rg.arrival, rg.departure, rg.status, rg.room_id, rg.rateplan_id, rg.shared_register_id, rg.share_rate, rg.split_folio FROM registration rg WHERE rg.id = $1 INTO _arrival, _old_departure, _status, _room_id, _rateplan_id, _shared_register_id, _share_rate, _split_folio; _sys := fn_system_date(); CASE WHEN _status IS NULL THEN RETURN fn_result_error('10701', 'Record not found (%s)', 'Registration'); WHEN _status = 'O' THEN RETURN fn_result_error('30111', 'Guest already checked out'); WHEN _status = 'X' THEN RETURN fn_result_error('30114', 'Check-in is already cancelled'); WHEN _old_departure = new_departure THEN RETURN fn_result_error('30132', 'The departure date is already the same'); -- new_departure = _arrival = day-use ถือว่าถูกต้อง ปล่อยผ่าน WHEN new_departure < _sys THEN RETURN fn_result_error('20101', 'Invalid departure date'); WHEN new_departure < _arrival THEN RETURN fn_result_error('20101', 'Invalid departure date'); ELSE END CASE; -- primary-only follower มี rate เป็น 0 ทั้งช่วงและอาศัย stay ของ primary -- departure ต้องเปลี่ยนที่ main guest แล้ว cascade ลงมา ห้ามแก้ตรง ๆ ไม่งั้น share group แตกกัน -- ล้อ guard เดิม 20108 (sp_update_guest_inhouse) / 20103 (sp_update_room_charge_schedule) IF _shared_register_id IS NOT NULL AND NOT _share_rate THEN RETURN fn_result_error('20108', 'Cannot update primary-only split-folio follower'); END IF; -- ขอบท้ายของช่วงพักใหม่: ปกติ = departure-1, day-use (arrival = departure) = departure _end_date := IIF(_arrival = new_departure, new_departure, new_departure - 1); -- ย่อวันลงต่ำกว่าคืนที่ลงบัญชีไปแล้วไม่ได้ เพราะ sp_save_registration_rates จะไม่ลบแถว posted -- (ตั้งใจ ดู V20260817.10.1) จะเหลือแถว posted + transactions ค้างหลัง departure -- ต้องให้ cashier ไป cancel auto-post ก่อน (rpc.cashier_folio_cancel_auto_post) -- เช็คทั้ง cascade set ก่อนเขียนอะไรเลย ไม่ให้ primary ถูกแก้แล้ว follower fail กลางทาง SELECT min(rr.charge_date) INTO _posted_date FROM registration_rates rr WHERE rr.posted AND rr.charge_date > _end_date AND (rr.register_id = $1 OR rr.register_id IN (SELECT f.id FROM registration f WHERE f.shared_register_id = $1 AND NOT f.share_rate AND f.status = 'I')); IF _posted_date IS NOT NULL THEN RETURN fn_result_error('30133', 'Unable to shorten the stay, room charge for %s has already been posted', to_char(_posted_date, 'DD-MM-YYYY')); END IF; CALL sp_check_room_activity(_room_id, _arrival, new_departure, register_id => $1); checkin_data := sp_get_guest_inhouse_js($1)::jsonb || '{"keep_original_rate": true}'::jsonb; verify_result := sp_verify_rate_and_available($1, _rateplan_id, _room_id, _arrival, new_departure, (checkin_data->>'adult')::int, (checkin_data->>'child')::int, (checkin_data->>'infant')::int, (checkin_data->>'extra_adult')::int, (checkin_data->>'extra_child')::int, (checkin_data->>'extra_infant')::int, checkin_data, true, true ); if verify_result->>'result_code' = '0' then checkin_data := verify_result->'result_data'; UPDATE registration rg SET departure = new_departure, modify_user = user_name, modify_time = current_timestamp WHERE rg.id = $1; -- p_validate = false สำหรับสมาชิก split folio: distribute member (รวม anchor เอง) ถือ -- FLOOR(full/n) และ primary-only follower ถือ 0 ซึ่งไม่ใช่ยอดที่ operation นี้เป็นคนตั้ง -- sp_validate_rate_amount ไม่มีคอนเซ็ปต์ของ split share จะฟ้อง 10801 ทั้งที่เรทถูกอยู่แล้ว PERFORM sp_save_registration_rates($1, (checkin_data->>'rate_id')::int, (checkin_data->>'arrival')::date, new_departure, checkin_data->'rate_schedule', p_validate := NOT _split_folio ); -- charge_schedule ของ requirement/inclusion ถูก materialize ไว้ตาม arrival/departure ตอนสร้าง -- เปลี่ยน departure แล้วต้อง re-expand ใหม่ ไม่งั้น ขยายวัน = คืนใหม่ไม่มี requirement charge, -- ย่อวัน = end-day ยัง post requirement เลยวัน departure -- sp_gen_request_charge_schedule อ่าน registration.arrival/departure สด และลบเฉพาะแถวที่ยังไม่ posted -- (ตัวเดียวกับที่ trigger tr_guest_request_gen_charges เรียก) PERFORM sp_gen_request_charge_schedule(gr.id) FROM guest_request gr WHERE gr.register_id = $1; -- ต้องอยู่ท้ายสุด หลัง registration.departure, registration_rates และ charge_schedule นิ่งแล้ว -- เพราะอ่านสถานะปัจจุบันของทั้งสามมาคำนวณ room_charge / extra_charge / other_charge PERFORM sp_update_registration_summary($1); -- sp_recapitulate แยกนับ extend_stay_rooms / early_checkout_rooms จาก title ของ log IF _old_departure < new_departure THEN PERFORM sp_log_inhouse($1, 'Extend stay', 'Extend stay from ' ||to_char(_old_departure, 'dd/MM/yyyy')||' to ' ||to_char(new_departure, 'dd/MM/yyyy'), user_name); ELSE PERFORM sp_log_inhouse($1, 'Change departure', 'Change departure from ' ||to_char(_old_departure, 'dd/MM/yyyy')||' to ' ||to_char(new_departure, 'dd/MM/yyyy'), user_name); END IF; -- cascade departure ลง primary-only follower (share_rate = false) -- เรียกเฉพาะเมื่อตัวเองเป็น primary; follower ถูก guard 20108 ตีกลับไปแล้วด้านบน IF _shared_register_id IS NULL THEN CALL sp_sync_split_folio_departure($1, new_departure, user_name); END IF; RETURN fn_result_success(); else RETURN verify_result; end if; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS _sqlstate = RETURNED_SQLSTATE, _msg_text = MESSAGE_TEXT, _detail = PG_EXCEPTION_DETAIL, _hint = PG_EXCEPTION_HINT, _context = PG_EXCEPTION_CONTEXT; RETURN fn_handle_error(_sqlstate, _msg_text, _detail, _hint, _context, 'sp_change_departure', jsonb_build_object('register_id',register_id, 'new_departure',new_departure) ); END