makea-cli 0.1.22__tar.gz → 0.1.24__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. {makea_cli-0.1.22 → makea_cli-0.1.24}/PKG-INFO +3 -1
  2. {makea_cli-0.1.22 → makea_cli-0.1.24}/README.md +2 -0
  3. makea_cli-0.1.24/makea_cli/__init__.py +1 -0
  4. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/api_client.py +19 -0
  5. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/commands/supplier/cmd.py +177 -0
  6. {makea_cli-0.1.22 → makea_cli-0.1.24}/pyproject.toml +1 -1
  7. makea_cli-0.1.22/makea_cli/__init__.py +0 -1
  8. {makea_cli-0.1.22 → makea_cli-0.1.24}/.claude/skills/create-supplier-profile/SKILL.md +0 -0
  9. {makea_cli-0.1.22 → makea_cli-0.1.24}/.claude/skills/lark-cli-setup/SKILL.md +0 -0
  10. {makea_cli-0.1.22 → makea_cli-0.1.24}/.claude/skills/link-supplier-user/SKILL.md +0 -0
  11. {makea_cli-0.1.22 → makea_cli-0.1.24}/.claude/skills/makea-cli-setup/SKILL.md +0 -0
  12. {makea_cli-0.1.22 → makea_cli-0.1.24}/.claude/skills/payment-revenue-report/SKILL.md +0 -0
  13. {makea_cli-0.1.22 → makea_cli-0.1.24}/.claude/skills/supplier-user-id-migration/SKILL.md +0 -0
  14. {makea_cli-0.1.22 → makea_cli-0.1.24}/.gitignore +0 -0
  15. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea-collection-upload/SKILL.md +0 -0
  16. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea-collection-upload/scripts/create_products.py +0 -0
  17. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/__main__.py +0 -0
  18. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/auth_pkce.py +0 -0
  19. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/commands/__init__.py +0 -0
  20. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/commands/_util.py +0 -0
  21. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/commands/analytics/__init__.py +0 -0
  22. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/commands/analytics/cmd.py +0 -0
  23. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/commands/auth/__init__.py +0 -0
  24. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/commands/auth/cmd.py +0 -0
  25. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/commands/designer/__init__.py +0 -0
  26. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/commands/designer/cmd.py +0 -0
  27. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/commands/directory/__init__.py +0 -0
  28. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/commands/directory/cmd.py +0 -0
  29. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/commands/document/__init__.py +0 -0
  30. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/commands/document/cmd.py +0 -0
  31. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/commands/email/__init__.py +0 -0
  32. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/commands/email/cmd.py +0 -0
  33. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/commands/library/__init__.py +0 -0
  34. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/commands/library/cmd.py +0 -0
  35. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/commands/metrics/__init__.py +0 -0
  36. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/commands/metrics/cmd.py +0 -0
  37. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/commands/orders/__init__.py +0 -0
  38. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/commands/orders/cmd.py +0 -0
  39. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/commands/product/__init__.py +0 -0
  40. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/commands/product/cmd.py +0 -0
  41. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/commands/production/__init__.py +0 -0
  42. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/commands/production/cmd.py +0 -0
  43. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/commands/production/pricing_spec.py +0 -0
  44. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/commands/supplier/__init__.py +0 -0
  45. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/config.py +0 -0
  46. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/main.py +0 -0
  47. {makea_cli-0.1.22 → makea_cli-0.1.24}/makea_cli/posthog_client.py +0 -0
  48. {makea_cli-0.1.22 → makea_cli-0.1.24}/npm/makea-cli/README.md +0 -0
  49. {makea_cli-0.1.22 → makea_cli-0.1.24}/npm/makea-cli/makea-cli.mjs +0 -0
  50. {makea_cli-0.1.22 → makea_cli-0.1.24}/npm/makea-cli/package.json +0 -0
  51. {makea_cli-0.1.22 → makea_cli-0.1.24}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: makea-cli
3
- Version: 0.1.22
3
+ Version: 0.1.24
4
4
  Summary: Makea admin CLI (browser login + Makea admin HTTP API)
5
5
  Requires-Python: >=3.10
6
6
  Requires-Dist: httpx>=0.27
@@ -80,6 +80,8 @@ makea-cli auth --logout
80
80
  - `makea-cli create-user <email> --user-type "<type>" [--password ...] [--name ...] [--company-name ...] [--username ...]` — 注册用户并直接确认 Cognito 账号(POST `/admin/users/create`,不发验证码邮件,创建后即可用该密码登录)
81
81
  - `makea-cli list-suppliers` — 列出供应商(需管理员权限)
82
82
  - `makea-cli create-quote-request -s <supplier_id> -p <product_reference_id> [--quote-type SAMPLING|PRODUCTION] [--message ...] [--due-date YYYY-MM-DD]` — 创建并发送 quote request(POST `/admin/supplier/create_quote_request`)
83
+ - `makea-cli get-quote-requests [--product-id <product_id>] [--quote-request-id <id>]` — 列某个产品下的 quote requests,或读单条完整内容(GET `/admin/quote_requests`)
84
+ - `makea-cli update-quote-request <quote_request_id> [--stage SAMPLING] [--status PENDING_SUPPLIER] [--due-date YYYY-MM-DD | --clear-due-date]` — 改 quote request 的 stage / status / 截止日(PATCH `/admin/quote_requests/{id}/update`)。**代供应商报价前常常要先用它**:quote request 停在 ENGAGEMENT(供应商还没接单)时 `submit-supplier-quote` 会被拒;过了 due date 也会被拒
83
85
  - `makea-cli get-quote-requests-by-supplier-id <supplier_id> [--status <status>] [-n <limit>]` — 直连后端按 supplier_id 查询 quote requests
84
86
  - `makea-cli get-supplier-links-by-supplier-id <supplier_id> [--link-type SAMPLING|PRODUCTION]` — 直连后端按 supplier_id 查询 supplier links
85
87
  - `makea-cli submit-supplier-quote <quote_request_id> --link-name ... --currency ... --sampling-price-value ... --sampling-price-valid-until ... --sample-lead-time ... --deliverables-json ... --customer-note ...` — 以 admin 身份代供应商向指定 quote request 新增 quote。大货阶梯价(`--production-price-tiers-json`)和 `--lead-time` 现在是可选的;样衣加做费、MOQ 口径、寄料地址(CMT / full package 打样报价必填)、可选尺码颜色、规格行、typed link 等字段见 `--help`。pricing groups 提交时给不进去,提交完用 `update-supplier-quote` 补
@@ -69,6 +69,8 @@ makea-cli auth --logout
69
69
  - `makea-cli create-user <email> --user-type "<type>" [--password ...] [--name ...] [--company-name ...] [--username ...]` — 注册用户并直接确认 Cognito 账号(POST `/admin/users/create`,不发验证码邮件,创建后即可用该密码登录)
70
70
  - `makea-cli list-suppliers` — 列出供应商(需管理员权限)
71
71
  - `makea-cli create-quote-request -s <supplier_id> -p <product_reference_id> [--quote-type SAMPLING|PRODUCTION] [--message ...] [--due-date YYYY-MM-DD]` — 创建并发送 quote request(POST `/admin/supplier/create_quote_request`)
72
+ - `makea-cli get-quote-requests [--product-id <product_id>] [--quote-request-id <id>]` — 列某个产品下的 quote requests,或读单条完整内容(GET `/admin/quote_requests`)
73
+ - `makea-cli update-quote-request <quote_request_id> [--stage SAMPLING] [--status PENDING_SUPPLIER] [--due-date YYYY-MM-DD | --clear-due-date]` — 改 quote request 的 stage / status / 截止日(PATCH `/admin/quote_requests/{id}/update`)。**代供应商报价前常常要先用它**:quote request 停在 ENGAGEMENT(供应商还没接单)时 `submit-supplier-quote` 会被拒;过了 due date 也会被拒
72
74
  - `makea-cli get-quote-requests-by-supplier-id <supplier_id> [--status <status>] [-n <limit>]` — 直连后端按 supplier_id 查询 quote requests
73
75
  - `makea-cli get-supplier-links-by-supplier-id <supplier_id> [--link-type SAMPLING|PRODUCTION]` — 直连后端按 supplier_id 查询 supplier links
74
76
  - `makea-cli submit-supplier-quote <quote_request_id> --link-name ... --currency ... --sampling-price-value ... --sampling-price-valid-until ... --sample-lead-time ... --deliverables-json ... --customer-note ...` — 以 admin 身份代供应商向指定 quote request 新增 quote。大货阶梯价(`--production-price-tiers-json`)和 `--lead-time` 现在是可选的;样衣加做费、MOQ 口径、寄料地址(CMT / full package 打样报价必填)、可选尺码颜色、规格行、typed link 等字段见 `--help`。pricing groups 提交时给不进去,提交完用 `update-supplier-quote` 补
@@ -0,0 +1 @@
1
+ __version__ = "0.1.24"
@@ -294,6 +294,25 @@ def get_quote_requests(*, product_id: str | None = None) -> Any:
294
294
  return get_json("/admin/quote_requests", params=params)
295
295
 
296
296
 
297
+ def get_quote_request(*, quote_request_id: str) -> Any:
298
+ """
299
+ GET /admin/quote_requests/{quote_request_id} — one quote request in full.
300
+ """
301
+ return get_json(f"/admin/quote_requests/{quote_request_id}")
302
+
303
+
304
+ def update_quote_request(
305
+ *,
306
+ quote_request_id: str,
307
+ body: dict[str, Any],
308
+ ) -> Any:
309
+ """
310
+ PATCH /admin/quote_requests/{quote_request_id}/update — set a quote request's
311
+ stage, status and/or due date. Only the keys present in ``body`` are applied.
312
+ """
313
+ return patch_json(f"/admin/quote_requests/{quote_request_id}/update", body)
314
+
315
+
297
316
  def create_quote_request(
298
317
  *,
299
318
  supplier_id: str,
@@ -22,6 +22,32 @@ def _normalize_add_supplier_body(raw: dict) -> dict:
22
22
 
23
23
  MOQ_APPLIES_TO_VALUES = ("style", "size", "color")
24
24
 
25
+ # Mirrors garmentby_service.domain.QuoteRequest — kept here only so `--help` can
26
+ # name the values; the backend is the one that validates.
27
+ QUOTE_REQUEST_STAGES = ("ENGAGEMENT", "SAMPLING", "PRODUCTION")
28
+ QUOTE_REQUEST_STATUSES = (
29
+ "PENDING_MERCHANT",
30
+ "PENDING_SUPPLIER",
31
+ "NEEDS_CLARIFICATION",
32
+ "COMPLETED",
33
+ "ORDER_PLACED",
34
+ "TERMINATED",
35
+ "RESAMPLE_QUOTE_REQUESTED",
36
+ )
37
+ PRODUCTION_FLOW_STATUSES = (
38
+ "PENDING_QUOTE",
39
+ "QUOTE_SUBMITTED",
40
+ "QUOTE_CONFIRMED",
41
+ "AWAITING_DEPOSIT",
42
+ "DEPOSIT_PAID",
43
+ "IN_PRODUCTION",
44
+ "AWAITING_FINAL_PAYMENT",
45
+ "FINAL_PAYMENT_PAID",
46
+ "SHIPPED",
47
+ "COMPLETED",
48
+ "CANCELLED",
49
+ )
50
+
25
51
 
26
52
  def _parse_json_option(raw: str, option_name: str) -> object:
27
53
  """Parse a JSON-valued option, failing with the option's own name."""
@@ -61,6 +87,14 @@ def _validate_moq_applies_to(values: list[str]) -> list[str]:
61
87
  return [v for v in MOQ_APPLIES_TO_VALUES if v in set(normalized)]
62
88
 
63
89
 
90
+ def _validate_choice(value: str, allowed: tuple[str, ...], option_name: str) -> str:
91
+ """Reject an out-of-range option before it costs a round trip."""
92
+ if value not in allowed:
93
+ console.print(f"[red]{option_name} must be one of {', '.join(allowed)}.[/red]")
94
+ raise typer.Exit(1)
95
+ return value
96
+
97
+
64
98
  def _none_if_null_literal(value: str) -> str | None:
65
99
  """Let an option clear a stored value by passing the literal `null`."""
66
100
  return None if value.strip().lower() in ("null", "none") else value
@@ -514,6 +548,124 @@ def register(app: typer.Typer) -> None:
514
548
  ),
515
549
  )
516
550
 
551
+ @app.command(
552
+ "get-quote-requests",
553
+ help=(
554
+ "GET /admin/quote_requests — list quote requests, optionally for one product. "
555
+ "Pass --quote-request-id to read a single one in full "
556
+ "(GET /admin/quote_requests/{id})."
557
+ ),
558
+ )
559
+ def get_quote_requests(
560
+ product_id: str | None = typer.Option(
561
+ None,
562
+ "--product-id",
563
+ "-p",
564
+ help="product_id: only quote requests on this product (product_reference_id).",
565
+ ),
566
+ quote_request_id: str | None = typer.Option(
567
+ None,
568
+ "--quote-request-id",
569
+ help="quote_request_id: read this one quote request in full instead of a list.",
570
+ ),
571
+ ) -> None:
572
+ """
573
+ List quote requests, or read one.
574
+ """
575
+ if quote_request_id:
576
+ print_json_or_exit(
577
+ console,
578
+ lambda: api_client.get_quote_request(quote_request_id=quote_request_id),
579
+ )
580
+ return
581
+ print_json_or_exit(
582
+ console,
583
+ lambda: api_client.get_quote_requests(product_id=product_id),
584
+ )
585
+
586
+ @app.command(
587
+ "update-quote-request",
588
+ help=(
589
+ "PATCH /admin/quote_requests/{quote_request_id}/update — set a quote "
590
+ "request's stage, status and/or due date. Only what you pass is changed. "
591
+ "This is the door that unblocks a submit: submit-supplier-quote requires "
592
+ "stage SAMPLING or PRODUCTION, and a quote request sits in ENGAGEMENT "
593
+ "until the supplier accepts it."
594
+ ),
595
+ )
596
+ def update_quote_request(
597
+ quote_request_id: str = typer.Argument(
598
+ ...,
599
+ help="quote_request_id: the quote request to update.",
600
+ ),
601
+ stage: str | None = typer.Option(
602
+ None,
603
+ "--stage",
604
+ help=(
605
+ "current_stage: ENGAGEMENT (supplier has not accepted yet), SAMPLING or "
606
+ "PRODUCTION. Moving to SAMPLING/PRODUCTION by hand skips the supplier's "
607
+ "own acceptance, so accepted_at / terms_accepted_at stay empty."
608
+ ),
609
+ ),
610
+ status: str | None = typer.Option(
611
+ None,
612
+ "--status",
613
+ help=(
614
+ "status: for ENGAGEMENT/SAMPLING one of "
615
+ f"{', '.join(QUOTE_REQUEST_STATUSES)}; "
616
+ "for PRODUCTION one of "
617
+ f"{', '.join(PRODUCTION_FLOW_STATUSES)}. "
618
+ "The backend validates against whichever stage the request ends up in."
619
+ ),
620
+ ),
621
+ due_date: str | None = typer.Option(
622
+ None,
623
+ "--due-date",
624
+ help=(
625
+ "due_date: ISO date (YYYY-MM-DD). A quote request past its due date "
626
+ "cannot be quoted, so this is the other thing that blocks a submit."
627
+ ),
628
+ ),
629
+ clear_due_date: bool = typer.Option(
630
+ False,
631
+ "--clear-due-date",
632
+ help="Send due_date as null, leaving the quote request with no deadline.",
633
+ ),
634
+ ) -> None:
635
+ """
636
+ Update a quote request's stage, status and/or due date.
637
+ """
638
+ if due_date and clear_due_date:
639
+ console.print("[red]Pass either --due-date or --clear-due-date, not both.[/red]")
640
+ raise typer.Exit(1)
641
+
642
+ body: dict[str, object] = {}
643
+ if stage is not None:
644
+ body["current_stage"] = _validate_choice(
645
+ stage.strip().upper(), QUOTE_REQUEST_STAGES, "--stage"
646
+ )
647
+ if status is not None:
648
+ body["status"] = status.strip().upper()
649
+ if due_date:
650
+ body["due_date"] = due_date.strip()
651
+ elif clear_due_date:
652
+ body["due_date"] = None
653
+
654
+ if not body:
655
+ console.print(
656
+ "[red]Nothing to update: pass --stage, --status, --due-date or "
657
+ "--clear-due-date.[/red]"
658
+ )
659
+ raise typer.Exit(1)
660
+
661
+ print_json_or_exit(
662
+ console,
663
+ lambda: api_client.update_quote_request(
664
+ quote_request_id=quote_request_id,
665
+ body=body,
666
+ ),
667
+ )
668
+
517
669
  @app.command(
518
670
  "submit-supplier-quote",
519
671
  help=(
@@ -705,6 +857,17 @@ def register(app: typer.Typer) -> None:
705
857
  "--refundable-terms-json",
706
858
  help="refundable_terms: refundable terms (optional, JSON object/array string).",
707
859
  ),
860
+ ip_schedule_json: str | None = typer.Option(
861
+ None,
862
+ "--ip-schedule-json",
863
+ help=(
864
+ "ip_schedule: who owns each asset the job creates (optional, JSON object). "
865
+ "Omit when the factory did not state it — makea must not invent an answer "
866
+ "on its behalf. If supplied it has to be COMPLETE (every one of the 12 "
867
+ "assets answered, plus production location and subcontractors on a "
868
+ "PRODUCTION link) or the API returns 400 listing what is missing."
869
+ ),
870
+ ),
708
871
  resample_request_id: str | None = typer.Option(
709
872
  None,
710
873
  "--resample-request-id",
@@ -795,6 +958,8 @@ def register(app: typer.Typer) -> None:
795
958
  body["refundable_terms"] = _parse_json_option(
796
959
  refundable_terms_json, "--refundable-terms-json"
797
960
  )
961
+ if ip_schedule_json is not None:
962
+ body["ip_schedule"] = _parse_json_option(ip_schedule_json, "--ip-schedule-json")
798
963
  if resample_request_id is not None:
799
964
  body["resample_request_id"] = resample_request_id
800
965
  if original_link_id is not None:
@@ -943,6 +1108,16 @@ def register(app: typer.Typer) -> None:
943
1108
  "--refundable-terms-json",
944
1109
  help="refundable_terms: refundable terms (JSON object; pass `null` to clear).",
945
1110
  ),
1111
+ ip_schedule_json: str | None = typer.Option(
1112
+ None,
1113
+ "--ip-schedule-json",
1114
+ help=(
1115
+ "ip_schedule: who owns each asset the job creates (JSON object; pass "
1116
+ "`null` to clear). A supplied schedule must be COMPLETE — the API "
1117
+ "refuses a half-answered one, so a quote can never carry a schedule "
1118
+ "the factory could not have submitted itself."
1119
+ ),
1120
+ ),
946
1121
  available_sizes_json: str | None = typer.Option(
947
1122
  None,
948
1123
  "--available-sizes-json",
@@ -1041,6 +1216,8 @@ def register(app: typer.Typer) -> None:
1041
1216
  override["one_time_production_fees"] = _parse_json_list_option(
1042
1217
  one_time_production_fees_json, "--one-time-production-fees-json"
1043
1218
  )
1219
+ if ip_schedule_json is not None:
1220
+ override["ip_schedule"] = _parse_json_option(ip_schedule_json, "--ip-schedule-json")
1044
1221
  if refundable_terms_json is not None:
1045
1222
  override["refundable_terms"] = _parse_json_option(
1046
1223
  refundable_terms_json, "--refundable-terms-json"
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "makea-cli"
7
- version = "0.1.22"
7
+ version = "0.1.24"
8
8
  description = "Makea admin CLI (browser login + Makea admin HTTP API)"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -1 +0,0 @@
1
- __version__ = "0.1.22"
File without changes
File without changes
File without changes