Skip to content

US-H06: Nhân bản pipeline trong cùng portal ​

Thuộc FS: 00-overview.md — SAPP HubSpot tools (sapp.001–004) Phần chung (tổng quan, roles, flow tổng, related features, NFR, Open Questions, References) xem ở Overview. File này chỉ chứa nội dung riêng của US. As-built tại commit e77c47a. Nguồn: inventory A-08 (dev), A-09 (prod) và A-11 cho popup lỗi. Danh sách pipeline (M1) mô tả đầy đủ ở US-H04.

⚠️ US này có đường ghi PROD (Duplicate khi Environment = Prod). Việc đó là cố ý hay bug chưa được xác nhận — OQ-H-03. Tài liệu mô tả đúng hiện trạng, không khẳng định là thiết kế.

FieldValue
US IDUS-H06
Role thực hiệnR1 - Người dùng tool (Assumption — OQ-H-01 về G-01)
PhaseAs-built (đã triển khai)
PriorityN/A — as-built
StatusDraft
NFR liên quanNFR-H-01, NFR-H-02, NFR-H-04, NFR-H-05, NFR-H-07 — xem Overview §5
OQ liên quanOQ-H-01, OQ-H-02, OQ-H-03, OQ-H-04, OQ-H-10, OQ-H-13, OQ-H-14, OQ-H-15 — xem Overview §6

1. US Statement ​

text
As a Người dùng tool  [Assumption — OQ-H-01 về G-01]
I want nhân bản một pipeline Deal / Ticket (kèm toàn bộ stage) thành pipeline mới với label mới, trong chính portal đang chọn
So that [Assumption — OQ-H-04] có sẵn một pipeline cùng cấu trúc để thử nghiệm mà không sửa pipeline gốc

2. Dependencies & Definition Of Ready ​

N/A — đã triển khai (Mode B). Dependency runtime:

#Cần sẵn sàngAi cung cấpTrạng thái
D1Tool sapp.004 đang bật / hiển thị / đã mở khoá — US-P02Admin CMSRuntime
D2env_systems: token của portal đang chọn (Dev → SAPP_DEV_HUBSPOT_ACCESS_TOKEN; Prod → SAPP_PROD_HUBSPOT_ACCESS_TOKEN) — US-P04Admin CMSRuntime
D3Danh sách pipeline đã tải ở US-H04—Runtime

3. Business Rules ​

IDRuleÁp dụng ởĐã cover bởi (AC)Nguồn code (Mode B/C)
BR-01Nhân bản luôn trong cùng portal: nguồn và đích đều là portal đang chọn ở Environment. Với Environment = Dev: nút "Duplicate" có trên mỗi thẻ pipeline và pipeline mới được tạo ở devM1 - action A1; M2 - action A2AC-M1.1, AC-M2.1fe/src/ui/components/works/sapp/pipeline-tool.tsx:337-353,209-214; be/apps/api/src/sapp/sapp.controller.ts:993-1004,1339-1350
BR-02Modal "Duplicate pipeline", phụ đề "Source: <label nguồn> · into <env> portal"; ô "New label" điền sẵn <label nguồn> (copy)M2 - field New labelAC-M2.1fe/src/ui/components/works/sapp/pipeline-tool.tsx:339-346,426-433,471-485
BR-03Trên màn, label bắt buộc (sau khi cắt khoảng trắng); trống → popup "Missing label", không gọi APIM2 - action A2AC-M2.2fe/src/ui/components/works/sapp/pipeline-tool.tsx:201-208
BR-04Không ghi đè: pipeline đích so khớp theo label (chính xác) trong cùng portal; đã có → trả nguyên pipeline đó, không tạo (NFR-H-07)M2 - action A2AC-M2.3be/libs/modules/src/lib/hubspot/common.service.ts:1068,1081-1084
BR-05Pipeline mới giữ thứ tự hiển thị và toàn bộ stage của nguồn (label, thứ tự, metadata); id pipeline / stage do HubSpot cấp mớiM2 - action A2AC-M2.1be/libs/modules/src/lib/hubspot/common.service.ts:1034-1046,1086-1087
BR-06Thành công → đóng modal, popup "Duplicate completed — Pipeline "<label>" was created in the <env> portal.", rồi tự tải lại danh sách của portal / object đang chọnM2 - action A2AC-M2.1fe/src/ui/components/works/sapp/pipeline-tool.tsx:166-179
BR-07Lỗi: nguồn không có → 12007; đọc danh sách đích lỗi → 12002; tạo lỗi → 12008. Popup "Failed to duplicate pipeline" (NFR-H-04)M2 - action A2EC-M2.1be/libs/modules/src/lib/hubspot/common.service.ts:1018-1023,1075-1079,1093-1098; fe/src/ui/components/works/sapp/pipeline-tool.tsx:165
BR-08Khi đang nhân bản: nút "Cancel", "Duplicate" và ô nhập bị khoá, modal không đóng đượcM2AC-M2.4fe/src/ui/components/works/sapp/pipeline-tool.tsx:185,434-439,448,456,486

⚠️ Observed (không phải BR):

  • Nút "Duplicate" cũng hiện khi Environment = Prod và ghi vào portal HubSpot prod — A1b / A2 nhánh Prod, AC-M2.5, OQ-H-03.
  • Popup "… was created …" kể cả khi không tạo gì (đích đã có) — AC-M2.3, OQ-H-14.
  • Gọi API trực tiếp với label rỗng → trả pipeline nguồn như thành công — EC-M2.4, OQ-H-10.
  • Danh sách không làm mới khi đổi Environment — EC-M1.1, OQ-H-15.

4. Design &ZeroWidthSpace;

a. Mối liên hệ giữa các màn & User Flow &ZeroWidthSpace;

text
[M1: Pipeline manager, danh sách đã tải]  (US-H04)
  ↓ bấm "Duplicate" trên một pipeline  (Environment = Dev  hoặc  Environment = Prod ⚠️)
[M2: Modal "Duplicate pipeline" — "into <env> portal"]
  ├─ Cancel ────────────────────────────→ đóng modal
  ├─ label trống ───────────────────────→ popup "Missing label", modal vẫn mở
  ├─ chưa có label đích → tạo ──────────→ popup "Duplicate completed", đóng modal, tải lại danh sách
  │                                        (Dev → ghi dev · Prod → ⚠️ GHI PROD)
  ├─ đã có label đích → không tạo ──────→ popup "Duplicate completed … was created" (⚠️ Observed)
  └─ lỗi (12001/12002/12007/12008) ─────→ popup "Failed to duplicate pipeline", modal vẫn mở

Link flow chi tiết: N/A — as-built, không có Figma.

b. Chi tiết từng màn &ZeroWidthSpace;

Màn M1 - Pipeline manager (nút "Duplicate") &ZeroWidthSpace;

FieldValue
Figma frameN/A
Trigger vào mànNhư US-H04 M1, đã bấm "Load pipelines" (Prod hoặc Dev)
Mô tả ngắnMỗi thẻ pipeline có nút "Duplicate" mở modal M2, ở cả Prod và Dev. Hiển thị danh sách: xem US-H04 M1
UI States &ZeroWidthSpace;
StateKhi nàoHiển thịFigma frame
DefaultDanh sách đã tảiNút "Duplicate" trên mỗi thẻ có id (Prod: cạnh "Clone to dev"; Dev: nút ghi duy nhất)N/A
LoadingĐang nhân bản / tải lại sau nhân bảnMọi nút / ô chọn bị khoá (US-H04 BR-07)N/A
Has data—Như US-H04 M1; sau nhân bản thành công, danh sách có thêm pipeline mớiN/A
EmptyKhông có pipeline"No pipelines found." — không có nútN/A
Error—Xem M2N/A
DisabledPipeline không có idNút "Duplicate" bị khoáN/A
Fields &ZeroWidthSpace;

N/A — M1 không có field nhập riêng cho US này; Environment / Object type mô tả ở US-H04 M1.

Filter / Search / Sort &ZeroWidthSpace;

Xem US-H04 M1 — không đổi.

Actions &ZeroWidthSpace;
A1a - Mở modal Duplicate (Environment = Dev) &ZeroWidthSpace;
ItemDescription
TriggerEnvironment = Dev, bấm "Duplicate" trên một thẻ
BehaviorMở M2 với nguồn = thẻ đó, label điền sẵn <label> (copy), phụ đề "… · into dev portal" (BR-02). Không gọi API, không ghi
Success ResultM2 mở

Error cases:

CaseExpected Handling
Pipeline không có idNút bị khoá
A1b - Mở modal Duplicate (Environment = Prod) — ⚠️ Observed &ZeroWidthSpace;
ItemDescription
TriggerEnvironment = Prod, bấm "Duplicate" trên một thẻ
BehaviorMở M2 như A1a, phụ đề "… · into prod portal". Không có bước xác nhận riêng cho prod ngoài modal nhập label (fe/src/ui/components/works/sapp/pipeline-tool.tsx:317-353,431-433). Cố ý hay bug → OQ-H-03
Success ResultM2 mở

Error cases:

CaseExpected Handling
Pipeline không có idNút bị khoá
Acceptance Criteria của màn M1 &ZeroWidthSpace;
AC-M1.1 - Nút Duplicate ở Dev &ZeroWidthSpace;
gherkin
Given Environment = Dev và danh sách đã tải
Then mỗi thẻ pipeline có nút "Duplicate" và không có nút "Clone to dev"

Maps to:

  • Business Rule: BR-01
AC-M1.2 - Nút Duplicate ở Prod (hành vi hiện tại) &ZeroWidthSpace;
gherkin
Given Environment = Prod và danh sách đã tải
Then ⚠️ Observed: mỗi thẻ pipeline có cả nút "Clone to dev" và nút "Duplicate" (OQ-H-03)

Maps to:

  • OQ: OQ-H-03
Negative AC / Edge Cases của màn M1 &ZeroWidthSpace;
IDTình huốngHành vi mong đợi
EC-M1.1Tải danh sách ở Dev, đổi Environment sang Prod mà không tải lại, bấm "Duplicate"⚠️ Observed — hành vi hiện tại theo code: danh sách dev vẫn hiện; modal ghi "into prod portal"; xác nhận sẽ gọi duplicate trong prod với id lấy từ danh sách dev. Id không có ở prod → 12007; id trùng một pipeline prod → tạo pipeline trong prod (fe/src/ui/components/works/sapp/pipeline-tool.tsx:54-56,269,339-346,209-214). Phát hiện khi viết R3, chưa chạy thử — OQ-H-15
EC-M1.2Đổi Object type sau khi tải danh sáchTương tự EC-M1.1: nhân bản dùng object mới với id của danh sách cũ — OQ-H-15

Màn M2 - Modal "Duplicate pipeline" &ZeroWidthSpace;

FieldValue
Figma frameN/A
Trigger vào mànA1a hoặc A1b ở M1
Mô tả ngắnNhập label mới (bắt buộc) rồi xác nhận. Chú thích: "A pipeline with this label is created in the <env> portal, reusing the source stages. If one with the same label already exists it is returned unchanged." (fe/src/ui/components/works/sapp/pipeline-tool.tsx:499-503)
UI States &ZeroWidthSpace;
StateKhi nàoHiển thịFigma frame
DefaultVừa mởTiêu đề "Duplicate pipeline", phụ đề "Source: <label nguồn> · into <env> portal", ô "New label" = <label nguồn> (copy) (placeholder "e.g. Sales pipeline (copy)"), nút "Cancel", "Duplicate"N/A
LoadingĐang nhân bảnNút "Duplicating…"; Cancel, ô nhập, đóng modal không tác dụng (BR-08)N/A
Has dataN/A — thành công thì modal đóng—N/A
EmptyÔ label trốngBấm "Duplicate" → popup "Missing label" (BR-03)N/A
ErrorAPI lỗiPopup "Failed to duplicate pipeline"; modal vẫn mởN/A
DisabledĐang nhân bảnNhư LoadingN/A
Fields &ZeroWidthSpace;
FieldRequiredTypeDefaultFormatValidationError MessageVisibilityDepends On
New labelYes (trên màn)Text<label nguồn> (copy)Cắt khoảng trắng hai đầuBR-03, BR-04"Missing label" — "Please enter a label for the duplicated pipeline."AlwaysLà khoá so khớp tồn tại trong portal đích (BR-04)
Filter / Search / Sort &ZeroWidthSpace;

N/A — modal xác nhận.

Actions &ZeroWidthSpace;
A2 - Duplicate (xác nhận) &ZeroWidthSpace;
ItemDescription
TriggerBấm "Duplicate" trong modal hoặc nhấn Enter trong ô label
BehaviorKiểm BR-03 → gọi POST /api/v1/sapp/{dev|prod}/hubspot/{deals|tickets}/pipelines/:pipelineId/duplicate theo Environment đang chọn, body label. BE: đọc pipeline nguồn và danh sách pipeline của cùng portal; có pipeline cùng label → trả nguyên (BR-04); chưa có → tạo pipeline mới kèm stage (BR-05). Environment = Dev: ghi vào HubSpot portal dev. ⚠️ Ghi PROD — Environment = Prod: ghi vào HubSpot portal prod (portal CRM thật): tạo pipeline mới hiện ra cho mọi người dùng HubSpot prod (be/apps/api/src/sapp/sapp.controller.ts:956-977,1302-1323; be/libs/services/src/lib/hubspot/hubspot.service.ts:246-254). ⚠️ Observed — cố ý hay bug → OQ-H-03
Success ResultĐóng modal; popup "Duplicate completed — Pipeline "<label>" was created in the <env> portal."; tải lại danh sách (BR-06)

Error cases:

CaseExpected Handling
Label trốngPopup "Missing label" (BR-03), không gọi API
Pipeline nguồn không tồn tạiPopup "Failed to duplicate pipeline" — "HubSpot pipeline not found (12007)"
Đọc danh sách đích lỗiPopup … "HubSpot API request failed (12002)"
HubSpot từ chối tạoPopup … "Failed to clone HubSpot pipeline (12008)"
Thiếu token portalPopup … "(12001)" (NFR-H-02)
A3 - Cancel &ZeroWidthSpace;
ItemDescription
TriggerBấm "Cancel" hoặc đóng modal khi không đang nhân bản
BehaviorĐóng modal, xoá label; không gọi API
Success ResultVề M1

Error cases:

CaseExpected Handling
Đang nhân bảnKhông đóng được (BR-08)
Acceptance Criteria của màn M2 &ZeroWidthSpace;
AC-M2.1 - Nhân bản trong dev &ZeroWidthSpace;
gherkin
Given Environment = Dev, Object type = Deal, dev có pipeline "Sales Pipeline" (5 stage), chưa có "Sales Pipeline (copy)"
When người dùng bấm "Duplicate" trên "Sales Pipeline", giữ label "Sales Pipeline (copy)" và bấm "Duplicate"
Then portal dev có pipeline mới "Sales Pipeline (copy)" với 5 stage cùng label, thứ tự, metadata (id mới)
And pipeline gốc và portal prod không bị thay đổi
And modal đóng, popup "Duplicate completed — Pipeline "Sales Pipeline (copy)" was created in the dev portal."
And danh sách dev tự tải lại và có thêm pipeline mới

Maps to:

  • Business Rule: BR-01, BR-02, BR-05, BR-06
  • Field/Action: New label / A2
AC-M2.2 - Label trống &ZeroWidthSpace;
gherkin
Given modal Duplicate đang mở
When người dùng xoá hết ô "New label" (hoặc chỉ để khoảng trắng) và bấm "Duplicate"
Then hiện popup "Missing label" — "Please enter a label for the duplicated pipeline."
And không có lời gọi API nào, modal vẫn mở

Maps to:

  • Business Rule: BR-03
AC-M2.3 - Label đích đã tồn tại (hành vi hiện tại) &ZeroWidthSpace;
gherkin
Given portal đang chọn đã có pipeline label "Sales Pipeline (copy)"
When người dùng nhân bản "Sales Pipeline" với label "Sales Pipeline (copy)"
Then không có pipeline nào được tạo hay sửa
And ⚠️ Observed: popup vẫn "Duplicate completed — Pipeline "Sales Pipeline (copy)" was created in the <env> portal." (OQ-H-14)
And danh sách vẫn tự tải lại

Maps to:

  • Business Rule: BR-04, BR-06
  • NFR: NFR-H-07
  • OQ: OQ-H-14
AC-M2.4 - Khoá khi đang nhân bản &ZeroWidthSpace;
gherkin
Given người dùng đã bấm "Duplicate" trong modal
When yêu cầu chưa có kết quả
Then nút hiện "Duplicating…", ô label và "Cancel" bị khoá, modal không đóng được

Maps to:

  • Business Rule: BR-08
AC-M2.5 - Nhân bản trong prod (⚠️ Ghi PROD — hành vi hiện tại) &ZeroWidthSpace;
gherkin
Given Environment = Prod, Object type = Deal, danh sách prod đã tải, prod chưa có "Sales Pipeline (copy)"
When người dùng bấm "Duplicate" trên "Sales Pipeline" của prod, giữ label và bấm "Duplicate"
Then ⚠️ Ghi PROD: portal HubSpot prod có pipeline mới "Sales Pipeline (copy)" với toàn bộ stage của nguồn
And không có bước xác nhận riêng nào cho prod ngoài modal nhập label (phụ đề "into prod portal")
And popup "Duplicate completed — Pipeline "Sales Pipeline (copy)" was created in the prod portal."
And danh sách prod tự tải lại
# ⚠️ Observed — cố ý hay bug chưa xác nhận (OQ-H-03)

Maps to:

  • OQ: OQ-H-03
  • REL: REL-H-04
Negative AC / Edge Cases của màn M2 &ZeroWidthSpace;
IDTình huốngHành vi mong đợi
EC-M2.1Pipeline nguồn bị xoá sau khi tải danh sáchPopup "Failed to duplicate pipeline" — "(12007)"; modal vẫn mở (BR-07)
EC-M2.2Label đích khác nguồn chỉ ở hoa / thường, hoặc pipeline sẵn có mang khoảng trắng thừaSo khớp chính xác, chỉ cắt khoảng trắng label mới → tạo thêm pipeline (be/libs/modules/src/lib/hubspot/common.service.ts:1068,1081-1083) — OQ-H-13. Ở Prod, hệ quả là thêm pipeline trong prod
EC-M2.3Người dùng sửa label thành đúng label nguồn (vd "Sales Pipeline")Label đích trùng chính pipeline nguồn → trả pipeline nguồn, không tạo; popup vẫn "… was created …" (BR-04, ⚠️ Observed — OQ-H-14)
EC-M2.4Gọi API trực tiếp (không qua màn) với label rỗng / chỉ khoảng trắng⚠️ Observed — qua được validate; BE rơi về label nguồn, tìm thấy chính pipeline nguồn và trả nó như thành công, không tạo gì (be/libs/interface/src/lib/dto/hubspot/hubspot-pipeline.dto.ts:52-55; be/libs/modules/src/lib/hubspot/common.service.ts:1068,1081-1084) — OQ-H-10, Overview §2.2
EC-M2.5Gọi API duplicate prod trực tiếp, không đăng nhậpHoạt động (NFR-P-01) → ⚠️ Ghi PROD không qua gate FE — OQ-H-02, OQ-H-03

5. Cross-feature AC &ZeroWidthSpace;

AC-X.1 - Tool bị gate chặn &ZeroWidthSpace;

gherkin
Given tool sapp.004 đang bị ẩn / tắt / khoá mã bí mật
When người dùng mở /works/sapp/tools/hs-pipeline
Then hành vi theo gate của nền tảng (US-P02)
And route duplicate (dev và prod) vẫn gọi trực tiếp được (NFR-P-01, Overview §2.2)

Maps to:

  • REL-H-01 (Affects - Hub + gate tool)

AC-X.2 - Token theo portal đang chọn &ZeroWidthSpace;

gherkin
Given SAPP_PROD_HUBSPOT_ACCESS_TOKEN chưa cấu hình, token dev có
When người dùng nhân bản với Environment = Prod
Then nhận lỗi 12001 và không có gì được tạo; nhân bản với Environment = Dev vẫn hoạt động

Maps to:

  • REL-H-02 (Affects - Cấu hình env_systems)

AC-X.3 - Pipeline mới xuất hiện trong HubSpot prod &ZeroWidthSpace;

gherkin
Given nhân bản thành công với Environment = Prod (AC-M2.5)
When người dùng HubSpot prod mở danh sách pipeline Deal trên HubSpot
Then thấy pipeline mới "Sales Pipeline (copy)"  [suy từ code, chưa chạy thử]

Maps to:

  • REL-H-04 (Be Affected - HubSpot portal prod)