Quote Request Copy Rule Details

這份文件提供 POST /quote_requests/copy/{quote_request_id}.json 各規則的可編輯詳細版。

建議維護方式:

  • rule_catalog.md 放精簡索引
  • 這份檔案放詳細說明

API session / request ownership 驗證

  • 舊入口:QuoteRequestsController::copy()
  • 主要表:quote_requests
  • 同步 / 非同步:同步
  • 新專案現況:
    • QuoteRequests 其他 action 多半直接用 ApiValidation::assertApiSession()
    • QuoteRequests::copy() 為了對齊 legacy response shape,改由 CopyRequestHandler 自行處理 session / blocked user / invalid session
  • Parity 要求:必做
  • 測試:
    • tests/QuoteRequestCopyIntegrationTest.php::testReturnsInvalidRequestWhenQuoteRequestIdIsMissing()
    • tests/QuoteRequestCopyIntegrationTest.php::testReturnsInvalidRequestWhenSourceRequestIsNotOwned()
  • 備註:
    • 先驗 session
    • 再驗 quote_requests.id + user_id
    • 已觀察到 staging caller 使用:
      • POST /quote_requests/copy/{id}.json
      • header 帶 X-PRO360-Rest-Api-Key
      • header 帶 X-PRO360-User-Session-Token
      • body 可為空 multipart
    • 已觀察成功 response:
      • {"error":0,"message":"success","quote_request_id":"20213"}
    • quote_request_id response shape 目前觀察為字串
    • 新專案 integration test 已明確斷言 quote_request_idstring
    • staging DB baseline:
      • source request 20212
      • copied request 20213
      • 兩筆 quote_requests.user_id 都是 8617

hash 驗證複製

  • 舊入口:QuoteRequestsController::copy($id, $hash)
  • 主要表:security_hashes, quote_requests
  • 同步 / 非同步:同步
  • 新專案現況:
    • QuoteRequests::copy() 已有 hash 分支
    • CopyRequestHandler 會驗 SecurityHash::getByHash('QuoteRequest', $hash)
    • 會再比對 foreign_id === sourceRequestId
  • Parity 要求:必做
  • 測試:
    • tests/QuoteRequestCopyIntegrationTest.php::testCopiesRequestSuccessfullyViaHash()
    • tests/QuoteRequestCopyIntegrationTest.php::testFallsBackToSessionWhenHashIsInvalid()
    • tests/QuoteRequestCopyIntegrationTest.php::testReturnsInvalidSessionWhenHashIsInvalidAndNoSessionExists()
  • 備註:
    • 目前已觀察到的公開 caller 是 session flow,不是 hash flow
    • 新專案目前支援 body 帶 hash
    • 已對齊舊專案行為:
      • hash 成功就走 hash
      • hash 失敗時會 fallback session
      • hash 與 session 都失敗才回 error = 2

blocked user 擋單

  • 舊入口:BlockedUser->isBlocked($user_id)
  • 主要表:blocked_users
  • 同步 / 非同步:同步
  • 新專案現況:
    • 已有 Lib/Model/BlockedUser.php
    • search_add 也有使用
  • Parity 要求:必做
  • 測試:
    • tests/QuoteRequestCopyGuardIntegrationTest.php::testRejectsBlockedUser()
  • 備註:
    • 目前實測 response 形狀:
      • error = 5
      • message = Blocked User
      • 不包含 quote_request_id
    • 已用 legacy staging API 實測確認:
      • POST /quote_requests/copy/4906.json
      • response {"error":5,"message":"Blocked User"}
    • 新專案目前已對齊這個 response shape

reserve_type_a 過期不可複製

  • 舊入口:來源 request 的 quote_form_submission_fields.type = reserve_type_a
  • 主要表:quote_form_submission_fields
  • 同步 / 非同步:同步
  • 新專案現況:
    • QuoteRequests::view()QuoteBid::handleBidLimit() 已有相近判斷
  • Parity 要求:必做
  • 測試:
    • tests/QuoteRequestCopyGuardIntegrationTest.php::testRejectsExpiredReserveTypeARequest()
  • 備註:
    • 需用 copy 專用 guard 封裝

category 必須 active

  • 舊入口:檢查 quote_categories.is_active = 2
  • 主要表:quote_categories
  • 同步 / 非同步:同步
  • 新專案現況:
    • 已有 QuoteCategory model
  • Parity 要求:必做
  • 測試:
    • tests/QuoteRequestCopyGuardIntegrationTest.php::testRejectsInactiveCategory()
  • 備註:
    • 舊版不是只看 category 存在

防重點擊

  • 舊入口:Redis key copy_request__{id} 20 秒內限制
  • 主要表:Redis
  • 同步 / 非同步:同步
  • 新專案現況:
    • 已有 RedisUtil
  • Parity 要求:必做
  • 測試:待補獨立 case
  • 備註:
    • 已沿用舊 key 命名:copy_request__{sourceRequestId}
    • 目前整合測試會清這個 key 以避免案例互相干擾,但還沒有獨立驗「第二次連點會被擋」的 case

24 小時同 category 複製次數限制

  • 舊入口:同 user / category / 非 archived / 非 closed / 24h count < 3
  • 主要表:quote_requests
  • 同步 / 非同步:同步
  • 新專案現況:
    • 可直接 query quote_requests
  • Parity 要求:必做
  • 測試:
    • tests/QuoteRequestCopyGuardIntegrationTest.php::testRejectsWhenSameCategoryRequestCountReachedLimit()
  • 備註:
    • 需確認是否只算 copy 或所有 request

booking user fee 歸零

  • 舊入口:若 booking_shop_users 有 user,fee set 全部歸零
  • 主要表:booking_shop_users, quote_requests
  • 同步 / 非同步:同步
  • 新專案現況:
    • CopyRequestHandler::resolveFeeSet() 會在 booking user 時回傳全 0 fee
    • CopyRequestHandler::resolveMatchType() 會設 BOOKING_ADMIN_USER
  • Parity 要求:必做
  • 測試:
    • tests/QuoteRequestCopyIntegrationTest.php::testBookingUserCopyForcesBookingAdminMatchType()
    • tests/QuoteRequestCopyBaselineIntegrationTest.php::testBookingBaselineRequestRowsMatchExpectedCopyRelationship()
  • 備註:
    • 真實 baseline 14241 -> 14306 已驗 manual_fee/auto_fee/direct_fee/contact_charge_fee/site_fee = 0
    • baseline 也驗 manual_fee_discount = 1.0000 與 tag ["回客"]

一般 user fee set 設定

  • 舊入口:quote_categories_quote_subscription_types
  • 主要表:quote_categories_quote_subscription_types, quote_requests
  • 同步 / 非同步:同步
  • 新專案現況:
    • CopyRequestFeeUpdater 已接入 copy flow
    • QuoteFormField::getVariableQuoteFee() flat row 問題已修正
  • Parity 要求:必做
  • 測試:
    • tests/QuoteRequestCopyIntegrationTest.php::testCopiesBaseFeesAndExpiredOnForStableFixture()
    • tests/QuoteRequestCopyBaselineIntegrationTest.php::testBaselineRequestRowsMatchExpectedCopyRelationship()
  • 備註:
    • staging DB baseline 20212 -> 20213 已證實 copy 不是單純沿用來源 fee,也不是只抄 category 固定 fee。
      • 2026-04-29 實查為準:
        • source manual_fee=53, auto_fee=97, direct_fee=88, contact_charge_fee=96
        • copied manual_fee=81, auto_fee=111, direct_fee=101, contact_charge_fee=122
    • 已確認 fee 來源:
      • category 614 基本 fee set 是 88 / 97 / 88 / 160
      • quote_form_field_id = 6760 的 variable fee options 中,一般居家清潔 直接對應
        • manual_fee=101
        • auto_fee=111
        • direct_fee=101
        • contact_charge_fee=152
      • 這筆案例的 region 是 台北市2026-04-29 實查對應 discount 結果為 0.8
      • quote_group_variable_fees / quote_fee_adjust_maps / quote_group_variable_fee_logs 都沒有命中
    • 已修正 QuoteFormField::getVariableQuoteFee() 對 flat row 結構失效的問題
    • 目前已補穩定 fixture 的 fee integration test,但 staging baseline 仍需持續對表
    • 已補 tests/QuoteRequestCopyBaselineIntegrationTest.php20212 -> 20213 的 fee baseline

建立新 quote_request 主表

  • 舊入口:複製原 request 主欄位,重設部分欄位
  • 主要表:quote_requests
  • 同步 / 非同步:同步
  • 新專案現況:
    • 已用 QuoteRequestsDODBFactory::create() 建立 copied request
  • Parity 要求:必做
  • 測試:
    • tests/QuoteRequestCopyIntegrationTest.php::testCopiesOwnedRequestThroughRouter()
    • tests/QuoteRequestCopyBaselineIntegrationTest.php::testBaselineRequestRowsMatchExpectedCopyRelationship()
  • 備註:
    • copied request 會寫 copied_quote_request_id
    • 一般 copy 不沿用 source match_type
    • specific_user_tag 會清成 null

寫入 copied_quote_request_id

  • 舊入口:新 request 指向來源 request id
  • 主要表:quote_requests
  • 同步 / 非同步:同步
  • 新專案現況:
    • schema 已有欄位
  • Parity 要求:必做
  • 測試:
    • tests/QuoteRequestCopyIntegrationTest.php::testCopiesOwnedRequestThroughRouter()
    • tests/QuoteRequestCopyBaselineIntegrationTest.php::testBaselineRequestRowsMatchExpectedCopyRelationship()
  • 備註:
    • 這是 downstream 判讀的重要欄位

更新 send_client / request_lang_id

  • 舊入口:依 API key 與 header lang 寫入 request
  • 主要表:quote_requests
  • 同步 / 非同步:同步
  • 新專案現況:
    • CopyRequestHandler::createCopiedRequest() 直接寫入
  • Parity 要求:必做
  • 測試:
    • tests/QuoteRequestCopyBaselineIntegrationTest.php::testBaselineRequestRowsMatchExpectedCopyRelationship()
  • 備註:
    • baseline 20212 -> 20213send_client = webrequest_lang_id = 1

重建 block_provider_ids

  • 舊入口:來源 request 的 block_provider_ids 加上既有 quoted provider
  • 主要表:quote_requests, quote_bids
  • 同步 / 非同步:同步
  • 新專案現況:
    • CopyRequestHandler::buildBlockProviderIds() 已實作
  • Parity 要求:必做
  • 測試:
    • tests/QuoteRequestCopyIntegrationTest.php::testNonProductionCopyDoesNotAppendQuotedProviderIdsToBlockList()
  • 備註:
    • legacy controller 會把 consumer 歷史已報價 provider 加入 block list
    • 新版只有在 site_environment=production 時追加歷史已報價 provider
    • 非 production 環境目前刻意保留 source block_provider_ids,讓 integration test 不受 shared DB 歷史 bid 影響
    • 若要驗完整 legacy 行為,需要 production-like 環境或可控 fixture

強制重設 request flags

  • 舊入口:has_preserve = 0, is_allow_contact_charge = 0, is_expired = 0
  • 主要表:quote_requests
  • 同步 / 非同步:同步
  • 新專案現況:
    • create 時可直接指定
  • Parity 要求:必做
  • 測試:
    • tests/QuoteRequestCopyIntegrationTest.php::testCopiesOwnedRequestThroughRouter()
    • tests/QuoteRequestCopyBaselineIntegrationTest.php::testBaselineRequestRowsMatchExpectedCopyRelationship()
  • 備註:
    • 已明確斷言 has_preserve = 0
    • 已明確斷言 is_allow_contact_charge = 0
    • 已明確斷言 is_expired = 0

寫入 multi-request tag

  • 舊入口:ConstJobCenterLabel::MULTI_REQUEST
  • 主要表:quote_requests
  • 同步 / 非同步:同步
  • 新專案現況:
    • 已改成使用 ConstJobCenterLabel::MULTI_REQUEST
  • Parity 要求:必做
  • 測試:
    • tests/QuoteRequestCopyIntegrationTest.php::testCopiesOwnedRequestThroughRouter()
  • 備註:
    • staging DB baseline 20212 -> 20213 顯示 copied request tags = ["回客"]
    • ConstJobCenterLabel::MULTI_REQUEST 實際值就是 回客
    • 這項目前不是 code parity 差異,先前是被常數名稱誤導

abnormal / keyword / overseas phone 掃描

  • 舊入口:copy() 建單後會接異常掃描,命中時停配對
  • 主要表:abnormal_check_items, quote_request_pending_params, quote_requests, user_internal_logs
  • 同步 / 非同步:同步
  • 新專案現況:
    • CopyRequestHandler 已接 AbnormalScanWriter
    • is_scan_found = true 時不再跑 auto quote / HK task
  • Parity 要求:必做
  • 測試:
    • tests/QuoteRequestCopyAbnormalIntegrationTest.php::testPausesMatchingWhenForcedAbnormalScanFound()
    • tests/QuoteRequestCopyAbnormalIntegrationTest.php::testPausesMatchingForHighRiskForeignPhoneScan()
    • tests/QuoteRequestCopyAbnormalIntegrationTest.php::testReturnsSuccessButPausesMatchingForForcedOldKeywordWarning()
  • 備註:
    • forced abnormal scan 會寫 abnormal_check_items,並把 request match_type 改成 ABNORMAL_CHECK
    • foreign phone 高風險會寫 user_internal_logs.note = 提出需求: 使用海外電話,seon偵測高風險
    • forced old keyword warning 在 copy 目前仍回 success,但會停配對;不走 search_add 的 warning payload 形狀

recommend_source 紀錄

  • 舊入口:copy() 建單成功後,若 request 有帶 recommend_source 則補寫 log
  • 主要表:quote_request_recommend_logs
  • 同步 / 非同步:同步
  • 新專案現況:
    • CopyRequestHandler 已支援讀取 recommend_source
    • 目前支援 top-level recommend_source
    • 也兼容 QuoteRequest[recommend_source]
  • Parity 要求:有帶才必做
  • 測試:
    • tests/QuoteRequestCopyIntegrationTest.php::testWritesRecommendSourceLogWhenProvided()
  • 備註:
    • legacy 寫入欄位為:
      • quote_request_id
      • recommend_source
    • 不寫回 quote_requests

目前測試覆蓋

  • tests/QuoteRequestCopyIntegrationTest.php
    • session copy
    • hash copy
    • recommend_source log
    • cloned submission / fields
    • copied_quote_request_id
    • last_copied_on
    • CreateRequest
    • auto quote dispatcher 入口
    • HK newQuoteRequest task
  • tests/QuoteRequestCopyAbnormalIntegrationTest.php
    • abnormal pause matching
    • foreign phone high-risk pause matching
    • old keyword forced warning 在 copy 仍回 success 但停配對
  • tests/QuoteRequestCopyGuardIntegrationTest.php
    • blocked user
    • inactive category
    • expired reserve_type_a
    • 24h same-category limit

auto quote / matching baseline(staging reference 與新專案現況)

  • staging reference 20212 -> 20213
    • quote_request_match_infos
      • info_type = r
      • info_type = sc
      • info_type = tp
    • quote_bids
      • total_bids = 100
      • auto_quote_bids = 7
      • narrow_bids = 14
    • quote_activities
      • quote_activity_type_id = 1 (Create Request) -> 1
      • quote_activity_type_id = 2 (Submit Quote) -> 8
      • quote_activity_type_id = 4 (View Match) -> 27
      • quote_activity_type_id = 52 (Auto Quote Restore Balance) -> 7
      • quote_activity_type_id = 55 -> 1
    • task_queue_log
      • action = newQuoteRequest
      • value = 20213
  • auto quote bid 抽樣形狀(staging 20212 -> 20213 的 7 筆一致):
    • quote_status_id = 2
    • provider_status_id = 2
    • is_auto_quote = 1
    • is_narrow_match = 0
    • is_want_to_contact_provider = 0
    • total_site_fee = 111
    • pricing_unit = 小時
  • narrow match bid 抽樣形狀(staging 20212 -> 20213 前 10 筆中可見):
    • quote_status_id = 1
    • provider_status_id = 1
    • is_auto_quote = 0
    • is_narrow_match = 1
    • is_want_to_contact_provider = 1
    • total_site_fee = 101
    • pricing_unit = 小時
  • staging 聚合欄位驗證:
    • auto quote bids 的 created 都是 2026-04-27 04:44:22
    • narrow match bids 的 created 都是 2026-04-27 04:49:05
    • quote_activity_type_id = 2 包含 7 筆 param1 = auto_quote,另有 1 筆 contact-charge 相關 payload
    • quote_activity_type_id = 4 包含 25 筆 param2 = m300,另有 2 筆空 param2
    • quote_activity_type_id = 52param1 固定為 111
  • 新專案目前已確認的 matching 對齊點:
    • quote_request_match_infos 的同步主線已穩定寫出 r / sc / tp
    • manual handler 已補回 read_segment >= 9 分流,不再把這批 service 直接落成一般 bid
    • related general pool 已補回「其實屬於原 category 的 service 要移除」
    • additional narrow 已補回 legacy temp table score boost
    • narrow pool 已補回 self-owned service 過濾,不再只在最後建 bid 時才跳過 request owner 自己的 service
  • 新專案目前仍保留的驗證註記:
    • 20212 / 8617 這組 reference case 受 daily limit 與 repeated-copy 汙染影響,文件不再把「本地已和 20212 -> 20213 完全同數字」當成既成事實
    • 其他同 category 614 的乾淨樣本可能出現 15 narrow,原因是 source user 不一定同時也是 provider;2021214 narrow8617 自己的 quote_service_id = 9930 有關
    • 另外已確認 reference provider 資料也有漂移:
      • 針對 provider 8085,staging reference 20213 使用的是 quote_service_id = 9115
      • 但 live DB 目前 9115.confirmed_prohibited_type = 2
      • 現行 matching 共同條件為 confirmed_prohibited_type in (0, 3, 4),所以 9115 現在已不具候選資格
      • 同 provider 目前仍可進候選的是 quote_service_id = 9169
      • 因此今天即使重跑同一 source request,也不應再假設 narrow provider 集合必然與 20213 完全相同
    • 因此目前應區分:
      • 20212 -> 20213 是 staging reference baseline
      • 新專案規則是否已補齊,要看 code + test 與 runtime 差異解釋
      • 20212 -> 20213 是否能在今天的 live DB 上原樣重現,則另外受 provider 資料漂移影響
  • 測試註記:
    • 這兩類 bid shape 不再依賴不穩定 fixture 18063
    • 已改用 QuoteRequestCopyBaselineIntegrationTest 直接驗真實 baseline
    • 目前已補:
      • 一般 baseline 20212 -> 20213
      • booking baseline 14241 -> 14306
      • abnormal baseline 14691 -> 17216
      • keyword baseline 10650 -> 10652
    • 目前整包 QuoteRequestCopy* 測試為 OK (32 tests, 288 assertions),沒有 skip

建立 quote_form_submissions

  • 舊入口:為新 request 建一筆 submission
  • 主要表:quote_form_submissions
  • 同步 / 非同步:同步
  • 新專案現況:
    • FormSubmissionCloner::cloneToRequest() 已負責建立新 submission
  • Parity 要求:必做
  • 測試:
    • tests/QuoteRequestCopyIntegrationTest.php::testCopiesOwnedRequestThroughRouter()
  • 備註:
    • copy 不走 search_add payload writer,而是 copy 專用 clone writer

複製 quote_form_submission_fields

  • 舊入口:把來源 request 的 field rows 複製到新 request
  • 主要表:quote_form_submission_fields
  • 同步 / 非同步:同步
  • 新專案現況:
    • FormSubmissionCloner::cloneToRequest() 逐筆 insert source fields
  • Parity 要求:必做
  • 測試:
    • tests/QuoteRequestCopyIntegrationTest.php::testCopiesOwnedRequestThroughRouter()
    • tests/QuoteRequestCopyBaselineIntegrationTest.php::testBaselineRequestRowsMatchExpectedCopyRelationship()
  • 備註:
    • 欄位值、type、responses 都要對齊
    • staging DB baseline 20212 -> 20213
      • source / copied 都是 12 筆 field rows
      • reserve_type_b 也原樣複製

variable fee 更新

  • 舊入口:updateRequestVariableFee()
  • 主要表:quote_requests, quote_fee_adjust_maps, quote_group_variable_fee_logs
  • 同步 / 非同步:同步
  • 新專案現況:
    • CopyRequestFeeUpdater 已接入 copy flow
  • Parity 要求:必做
  • 測試:
    • tests/QuoteRequestCopyIntegrationTest.php::testCopiesBaseFeesAndExpiredOnForStableFixture()
    • tests/QuoteRequestCopyBaselineIntegrationTest.php::testBaselineRequestRowsMatchExpectedCopyRelationship()
  • 備註:
    • staging DB baseline 20212 -> 20213
      • 2026-04-29 實查為準,manual_fee_discount0.6000 變成 0.8000
      • 這表示 copy 後還有額外 fee 調整,不是只複製來源 request 欄位
    • 目前驗收欄位包含 site_feemanual_feeauto_feedirect_feecontact_charge_feemanual_fee_discount

reload language summary

  • 舊入口:reload_lang_summary()
  • 主要表:quote_requests.language_summary
  • 同步 / 非同步:同步
  • 新專案現況:
    • CopyRequestHandler::reloadSummary() 會清掉 summary cache 後重算
  • Parity 要求:必做
  • 測試:
    • tests/QuoteRequestCopyBaselineIntegrationTest.php::testBaselineRequestRowsMatchExpectedCopyRelationship()
  • 備註:
    • baseline 驗 form_summarylanguage_summaryjob_center_summary

abnormal / keyword scan

  • 舊入口:新戶、關鍵字、海外電話風控
  • 主要表:abnormal_check_items, user_internal_logs
  • 同步 / 非同步:同步
  • 新專案現況:
    • SearchAdd 已有 AbnormalScanWriter
  • Parity 要求:必做
  • 測試:
    • tests/QuoteRequestCopyAbnormalIntegrationTest.php::testPausesMatchingWhenForcedAbnormalScanFound()
    • tests/QuoteRequestCopyAbnormalIntegrationTest.php::testPausesMatchingForHighRiskForeignPhoneScan()
    • tests/QuoteRequestCopyAbnormalIntegrationTest.php::testReturnsSuccessButPausesMatchingForForcedOldKeywordWarning()
    • tests/QuoteRequestCopyBaselineIntegrationTest.php::testAbnormalBaselineRequestRowsMatchExpectedCopyRelationship()
    • tests/QuoteRequestCopyBaselineIntegrationTest.php::testKeywordBaselineRequestRowsMatchExpectedCopyRelationship()
  • 備註:
    • copy 命中 abnormal / keyword 時仍回 success,但會停配對

auto quote

  • 舊入口:handleNewQuoteRequestAutoQuote()
  • 主要表:quote_bids, quote_request_match_infos
  • 同步 / 非同步:同步
  • 新專案現況:
    • 已有 auto quote 主線
  • Parity 要求:必做
  • 測試:
    • tests/QuoteRequestCopyIntegrationTest.php::testDispatchesAutoQuoteAfterCopy()
    • tests/QuoteRequestCopyIntegrationTest.php::testSkipsAutoQuoteWhenSourceRequestWasOriginallyCopied()
    • tests/QuoteRequestCopyBaselineIntegrationTest.php::testBaselineCopiedRequestHasExpectedAutoQuoteBidShape()
    • tests/QuoteRequestCopyBaselineIntegrationTest.php::testBaselineCopiedRequestHasExpectedNarrowMatchBidShape()
    • tests/QuoteRequestCopyBaselineIntegrationTest.php::testBaselineCopiedRequestHasExpectedBidAndActivityAggregateShape()
  • 備註:
    • staging DB baseline 20212 -> 20213 已證實成功 copy 會立刻建立 auto quote:
      • source quote_bids: 100 筆,is_auto_quote = 17
      • copied quote_bids: 100 筆,is_auto_quote = 1 也有 7
      • copied request 的 auto quote fee 區間為 111
    • quote_activities 也同步出現 quote_activity_type_id = 2
      • 7 筆 param1 = auto_quote
      • 1 筆 contact-charge 相關 payload

HK newQuoteRequest task

  • 舊入口:HK area 不延後配對時補 task
  • 主要表:task_queue
  • 同步 / 非同步:非同步
  • 新專案現況:
    • 已有 TaskQueuenewQuoteRequest handler
  • Parity 要求:必做
  • 測試:已有 forced-HK integration test
  • 備註:
    • QuoteRequestCopyIntegrationTest.php 會以 QUOTE_REQUEST_COPY_TEST_FORCE_AREA=hk 驗成功 copy 後有 newQuoteRequest task
    • 已直接比對 legacy controller 與新 handler,HK 分支主線條件一致:
      • 都只在 !is_scan_found 時送 task
      • 都只在 area = hk 時送 task
    • 仍需真正 HK 區域 baseline,才能證明實際 HK 環境資料也完全一致
    • staging DB baseline 20212 -> 20213
      • task_queue_logaction = newQuoteRequest
      • source 20212 執行時間約在 10:04
      • copied 20213 執行時間約在 04:49
    • 這表示成功 copy 路徑會送出 newQuoteRequest 類型 task,不是只有同步建單

設定 expired_on

  • 舊入口:先看 due_date field,否則取 category request_expired_days
  • 主要表:quote_form_submission_fields, quote_requests, quote_categories
  • 同步 / 非同步:同步
  • 新專案現況:
    • CopyRequestHandler::resolveExpiredOn() 已接入 copy flow
  • Parity 要求:必做
  • 測試:
    • tests/QuoteRequestCopyIntegrationTest.php::testCopiesBaseFeesAndExpiredOnForStableFixture()
    • tests/QuoteRequestCopyIntegrationTest.php::testDueDateFieldOverridesCategoryExpiredOn()
    • tests/QuoteRequestCopyBaselineIntegrationTest.php::testBaselineRequestRowsMatchExpectedCopyRelationship()
  • 備註:
    • copy 支援 due_date override
    • staging DB baseline 20212 -> 20213
      • source expired_on = 2026-05-08 10:00:00
      • copied expired_on = 2026-05-11 04:00:00
    • copied request 並沒有單純沿用來源 expired_on

建立 CreateRequest activity

  • 舊入口:createQuoteRequestActivityById(...CreateRequest)
  • 主要表:quote_activities
  • 同步 / 非同步:同步
  • 新專案現況:
    • 已有 QuoteActivity / RequestActivityWriter
  • Parity 要求:必做
  • 測試:
    • tests/QuoteRequestCopyIntegrationTest.php::testCopiesOwnedRequestThroughRouter()
    • tests/QuoteRequestCopyBaselineIntegrationTest.php::testBaselineRequestRowsMatchExpectedCopyRelationship()
  • 備註:
    • staging DB baseline 20212 -> 20213
      • copied request 有 quote_activity_type_id = 1
      • 但實際寫入順序上,auto quote activity 可能先於 CreateRequest
    • 如果新專案要追到 legacy parity,不能假設 CreateRequest 一定是第一筆 activity

更新來源 request last_copied_on

  • 舊入口:copy 成功後寫回來源 request
  • 主要表:quote_requests
  • 同步 / 非同步:同步
  • 新專案現況:
    • schema 已有欄位
  • Parity 要求:必做
  • 測試:
    • tests/QuoteRequestCopyIntegrationTest.php::testCopiesOwnedRequestThroughRouter()
    • tests/QuoteRequestCopyBaselineIntegrationTest.php::testBaselineRequestRowsMatchExpectedCopyRelationship()
  • 備註:
    • allow_copy 會讀這欄
    • staging DB baseline 20212 -> 20213
      • source 20212.last_copied_on = 2026-04-27 04:44:23
      • copied 20213.last_copied_on = null

recommend source 補充

  • 舊入口:從 checklist 來源寫 quote_request_recommend_logs
  • 主要表:quote_request_recommend_logs
  • 同步 / 非同步:同步
  • 新專案現況:
    • CopyRequestHandler::resolveRecommendSource() 已支援 copy 對應入口
  • Parity 要求:有帶才必做
  • 測試:
    • tests/QuoteRequestCopyIntegrationTest.php::testWritesRecommendSourceLogWhenProvided()
  • 備註:
    • 舊 controller 只看 top-level recommend_source
    • 新版另外兼容 QuoteRequest[recommend_source]