makea-cli 0.2.1__tar.gz → 0.2.2__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.
- {makea_cli-0.2.1 → makea_cli-0.2.2}/CHANGELOG.md +25 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/PKG-INFO +10 -1
- {makea_cli-0.2.1 → makea_cli-0.2.2}/README.md +9 -0
- makea_cli-0.2.2/makea_cli/__init__.py +1 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/api_client.py +46 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/brand/cmd.py +191 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/pyproject.toml +1 -1
- {makea_cli-0.2.1 → makea_cli-0.2.2}/tests/golden_manifest.json +283 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/tests/test_persona_commands.py +86 -0
- makea_cli-0.2.1/makea_cli/__init__.py +0 -1
- {makea_cli-0.2.1 → makea_cli-0.2.2}/.claude/skills/create-supplier-profile/SKILL.md +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/.claude/skills/lark-cli-setup/SKILL.md +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/.claude/skills/link-supplier-user/SKILL.md +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/.claude/skills/makea-cli-setup/SKILL.md +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/.claude/skills/payment-revenue-report/SKILL.md +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/.claude/skills/supplier-user-id-migration/SKILL.md +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/.gitignore +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea-collection-upload/SKILL.md +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea-collection-upload/scripts/create_products.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/__main__.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/auth_pkce.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/__init__.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/_util.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/analytics/__init__.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/analytics/cmd.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/auth/__init__.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/auth/cmd.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/brand/__init__.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/designer/__init__.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/designer/cmd.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/directory/__init__.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/directory/cmd.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/document/__init__.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/document/cmd.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/email/__init__.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/email/cmd.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/factory/__init__.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/factory/cmd.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/library/__init__.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/library/cmd.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/metrics/__init__.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/metrics/cmd.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/orders/__init__.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/orders/cmd.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/product/__init__.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/product/cmd.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/production/__init__.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/production/cmd.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/production/pricing_spec.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/supplier/__init__.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/commands/supplier/cmd.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/config.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/errors.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/identity.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/main.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/output.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/posthog_client.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/makea_cli/registry.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/npm/makea-cli/README.md +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/npm/makea-cli/makea-cli.mjs +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/npm/makea-cli/package.json +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/tests/test_output_contract.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/tests/test_registry.py +0 -0
- {makea_cli-0.2.1 → makea_cli-0.2.2}/uv.lock +0 -0
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.2
|
|
4
|
+
|
|
5
|
+
Order lifecycle, exposed in tiers rather than all at once.
|
|
6
|
+
|
|
7
|
+
- `ask` (an agent must put it in front of a person): `designer submit
|
|
8
|
+
sampling-order` (confirm a sample order), `designer submit production-order`,
|
|
9
|
+
`designer update production-order --complete|--links-file`, `designer update
|
|
10
|
+
shipping-order --confirm-receive`.
|
|
11
|
+
- `never` (an agent must not run it at all): `designer update sampling-order
|
|
12
|
+
--cancel`, `designer delete sampling-order` (DRAFT only, the backend enforces
|
|
13
|
+
that), `designer delete shipping-order` — the endpoint is named cancel but it
|
|
14
|
+
unlinks every production order and removes the shipment.
|
|
15
|
+
|
|
16
|
+
Cancel and delete are separate commands rather than flags on an `ask` one. The
|
|
17
|
+
grade is per command, so putting them on a milder command would launder a
|
|
18
|
+
destructive action through the wrong grade.
|
|
19
|
+
|
|
20
|
+
**The grade is advice to a harness, not a gate in the CLI.** A person in a
|
|
21
|
+
terminal runs any of these; `never` means an agent may not decide to.
|
|
22
|
+
|
|
23
|
+
Payments, bank transfers and subscriptions stay out of the CLI. The endpoints
|
|
24
|
+
exist, and a `--dry-run` would even make them look safe, but giving them a
|
|
25
|
+
command puts a charge one step away from an agent and no approval grade makes
|
|
26
|
+
that a good trade. A test asserts no write command reaches them.
|
|
27
|
+
|
|
3
28
|
## 0.2.1
|
|
4
29
|
|
|
5
30
|
Each persona can now clear its own queue. `list task` and `list action` could show
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: makea-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Makea admin CLI (browser login + Makea admin HTTP API)
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
Requires-Dist: click<9,>=8.1
|
|
@@ -79,6 +79,8 @@ makea-cli auth --logout
|
|
|
79
79
|
|
|
80
80
|
每条命令都有的开关:`--describe` 打印它的机器可读定义(flags、副作用、审批档位);`--json` 强制纯 JSON(stdout 不是终端时默认就是)。读命令还有 `--fields a,b,c` 只保留这些字段;`list` 命令还有 `--limit N`(后端自己分页的就透传,不分页的在本地截断并在结果里写明 `truncated` / `returned` / `omitted`);写命令还有 `--dry-run`(打印将要发出的请求,不发)和 `--idempotency-key <k>`。`makea-cli --describe --json` 打印全量 manifest。
|
|
81
81
|
|
|
82
|
+
写命令的**审批档位**(manifest 的 `approval`)是给 harness 的建议,不是 CLI 的门:`auto` 可以自动跑;`ask` 必须先让人确认;`never` agent 不许自己决定(人在终端照常能跑)。动钱的端点(payments / bank transfer / subscription)**故意不进 CLI**。
|
|
83
|
+
|
|
82
84
|
exit code:`0` 成功 · `2` 用法错 · `3` 未登录或身份与 persona 不匹配 · `4` 后端拒绝(4xx,错误 JSON 在 stderr)· `5` 没跑完(5xx / 网络 / 超时)——读命令直接重试即可;**写命令可能已经生效了,重试前先读一遍状态**。
|
|
83
85
|
|
|
84
86
|
0.1 的平铺命令名(`list-users`、`submit-supplier-quote` …)仍可用,作为隐藏的过渡别名转发到新命令并在 stderr 打一行提示;**下一个 minor 版本删除**。完整的改名对照表在 [CHANGELOG.md](CHANGELOG.md)。
|
|
@@ -147,6 +149,8 @@ exit code:`0` 成功 · `2` 用法错 · `3` 未登录或身份与 persona 不
|
|
|
147
149
|
| `makea-cli designer create product` | write(ask) | `POST /designer/product/create_or_update_product` | payload must match manufacture/specifications (all frontend fields; categories/subcategories + collection_id in --hel… | `designer-upsert-product` |
|
|
148
150
|
| `makea-cli designer create rfq` | write(ask) | `POST /designer/product/send_rfq` | ask the chosen suppliers to quote on a product. --supplier repeats; --makea-sourcing also asks Makea to source. | — |
|
|
149
151
|
| `makea-cli designer create sampling-order` | write(ask) | `POST /designer/sampling_orders` | order samples. --file is the full order payload (product_order_id, items, fees …); run --dry-run first and read the b… | — |
|
|
152
|
+
| `makea-cli designer delete sampling-order` | write(never) | `DELETE /designer/sampling_orders/{sampling_order_id}` | delete a DRAFT sample order. The backend refuses any other status. Graded `never`. | — |
|
|
153
|
+
| `makea-cli designer delete shipping-order` | write(never) | `POST /designer/shipping_orders/{shipping_order_id}/cancel` | the endpoint is called cancel but it unlinks every production order and removes the shipment, so it is a delete here.… | — |
|
|
150
154
|
| `makea-cli designer download document` | read | `GET /designer/document/download` | save bytes for user_id + document_id (designer-authenticated; same query contract as admin document download). | `designer-download-document` |
|
|
151
155
|
| `makea-cli designer get collection` | read | `GET /designer/collections/{collection_id}/overview` | one collection with its products, orders and lifecycle in a single read. | — |
|
|
152
156
|
| `makea-cli designer get lifecycle` | read | `GET /designer/product_orders/{product_order_id}/lifecycle` | which stage this product is in. | — |
|
|
@@ -167,10 +171,15 @@ exit code:`0` 成功 · `2` 用法错 · `3` 未登录或身份与 persona 不
|
|
|
167
171
|
| `makea-cli designer list task` | read | `GET /designer/tasks` | what the platform is waiting on you for. | — |
|
|
168
172
|
| `makea-cli designer search note` | read | `GET /designer/products/{product_id}/channel_search` | full-text search across a product's conversation; --channel narrows to one channel key. | — |
|
|
169
173
|
| `makea-cli designer set product-collection` | write(auto) | `POST /designer/products/{id}/link-to-collection` | assign product to a collection. | `designer-link-product-to-collection` |
|
|
174
|
+
| `makea-cli designer submit production-order` | write(ask) | `POST /designer/production_orders/{production_order_id}/submit` | submit a 大货 order. This is the commitment, so --dry-run and confirm with the user first. | — |
|
|
175
|
+
| `makea-cli designer submit sampling-order` | write(ask) | `POST /designer/sampling_orders/{sampling_order_id}/confirm` | confirm a sample order so the supplier starts. Run --dry-run first and read it back to the user. | — |
|
|
170
176
|
| `makea-cli designer update action` | write(auto) | `POST /designer/actions/{action_id}/{acknowledge\|snooze\|dismiss}` | respond to one of the pending actions from `designer list action`. An action is closed by the fact it asks for, not b… | — |
|
|
171
177
|
| `makea-cli designer update note` | write(ask) | `POST /designer/products/{product_id}/notes/{note_id}/{accept\|reject}` | decide on a task the assistant proposed on a product. | — |
|
|
172
178
|
| `makea-cli designer update product` | write(auto) | `POST /designer/product/update_basic_info` | patch fields on a product. --cover-image / --back-image take a document id and are what the assistant's set_product_i… | — |
|
|
179
|
+
| `makea-cli designer update production-order` | write(ask) | `POST /designer/production_orders/{production_order_id}/complete` | POST /designer/production_orders/{id}/complete, or PUT the order to re-pick which supplier links it covers (--links-f… | — |
|
|
173
180
|
| `makea-cli designer update proposal` | write(ask) | `POST /designer/beeboo_proposals/approve` | apply one spec change the assistant proposed. This edits the pack, so read the proposal first. | — |
|
|
181
|
+
| `makea-cli designer update sampling-order` | write(never) | `POST /designer/sampling_orders/{sampling_order_id}/cancel` | cancel a sample order. Graded `never`: an agent must not do this on its own, a person runs it. | — |
|
|
182
|
+
| `makea-cli designer update shipping-order` | write(ask) | `POST /designer/shipping_orders/{shipping_order_id}/confirm_receive` | say the goods arrived. The backend accepts this only while the shipment is IN_TRANSIT. | — |
|
|
174
183
|
| `makea-cli designer update suggested-action` | write(ask) | `POST /designer/channels/{channel_id}/suggested_actions/bulk_accept` | accept several of a channel's suggested actions in one write. --action takes note_id:action_id and repeats. | — |
|
|
175
184
|
| `makea-cli designer update task` | write(auto) | `POST /designer/tasks/{complete\|dismiss}` | close one of the tasks from `designer list task`. Complete means you did it; dismiss means it does not apply. | — |
|
|
176
185
|
| `makea-cli designer upload document` | write(auto) | `POST /designer/document/upload` | upload a file for the current designer token; optional product_id (omit to start a new draft id server-side). | `designer-upload-document` |
|
|
@@ -67,6 +67,8 @@ makea-cli auth --logout
|
|
|
67
67
|
|
|
68
68
|
每条命令都有的开关:`--describe` 打印它的机器可读定义(flags、副作用、审批档位);`--json` 强制纯 JSON(stdout 不是终端时默认就是)。读命令还有 `--fields a,b,c` 只保留这些字段;`list` 命令还有 `--limit N`(后端自己分页的就透传,不分页的在本地截断并在结果里写明 `truncated` / `returned` / `omitted`);写命令还有 `--dry-run`(打印将要发出的请求,不发)和 `--idempotency-key <k>`。`makea-cli --describe --json` 打印全量 manifest。
|
|
69
69
|
|
|
70
|
+
写命令的**审批档位**(manifest 的 `approval`)是给 harness 的建议,不是 CLI 的门:`auto` 可以自动跑;`ask` 必须先让人确认;`never` agent 不许自己决定(人在终端照常能跑)。动钱的端点(payments / bank transfer / subscription)**故意不进 CLI**。
|
|
71
|
+
|
|
70
72
|
exit code:`0` 成功 · `2` 用法错 · `3` 未登录或身份与 persona 不匹配 · `4` 后端拒绝(4xx,错误 JSON 在 stderr)· `5` 没跑完(5xx / 网络 / 超时)——读命令直接重试即可;**写命令可能已经生效了,重试前先读一遍状态**。
|
|
71
73
|
|
|
72
74
|
0.1 的平铺命令名(`list-users`、`submit-supplier-quote` …)仍可用,作为隐藏的过渡别名转发到新命令并在 stderr 打一行提示;**下一个 minor 版本删除**。完整的改名对照表在 [CHANGELOG.md](CHANGELOG.md)。
|
|
@@ -135,6 +137,8 @@ exit code:`0` 成功 · `2` 用法错 · `3` 未登录或身份与 persona 不
|
|
|
135
137
|
| `makea-cli designer create product` | write(ask) | `POST /designer/product/create_or_update_product` | payload must match manufacture/specifications (all frontend fields; categories/subcategories + collection_id in --hel… | `designer-upsert-product` |
|
|
136
138
|
| `makea-cli designer create rfq` | write(ask) | `POST /designer/product/send_rfq` | ask the chosen suppliers to quote on a product. --supplier repeats; --makea-sourcing also asks Makea to source. | — |
|
|
137
139
|
| `makea-cli designer create sampling-order` | write(ask) | `POST /designer/sampling_orders` | order samples. --file is the full order payload (product_order_id, items, fees …); run --dry-run first and read the b… | — |
|
|
140
|
+
| `makea-cli designer delete sampling-order` | write(never) | `DELETE /designer/sampling_orders/{sampling_order_id}` | delete a DRAFT sample order. The backend refuses any other status. Graded `never`. | — |
|
|
141
|
+
| `makea-cli designer delete shipping-order` | write(never) | `POST /designer/shipping_orders/{shipping_order_id}/cancel` | the endpoint is called cancel but it unlinks every production order and removes the shipment, so it is a delete here.… | — |
|
|
138
142
|
| `makea-cli designer download document` | read | `GET /designer/document/download` | save bytes for user_id + document_id (designer-authenticated; same query contract as admin document download). | `designer-download-document` |
|
|
139
143
|
| `makea-cli designer get collection` | read | `GET /designer/collections/{collection_id}/overview` | one collection with its products, orders and lifecycle in a single read. | — |
|
|
140
144
|
| `makea-cli designer get lifecycle` | read | `GET /designer/product_orders/{product_order_id}/lifecycle` | which stage this product is in. | — |
|
|
@@ -155,10 +159,15 @@ exit code:`0` 成功 · `2` 用法错 · `3` 未登录或身份与 persona 不
|
|
|
155
159
|
| `makea-cli designer list task` | read | `GET /designer/tasks` | what the platform is waiting on you for. | — |
|
|
156
160
|
| `makea-cli designer search note` | read | `GET /designer/products/{product_id}/channel_search` | full-text search across a product's conversation; --channel narrows to one channel key. | — |
|
|
157
161
|
| `makea-cli designer set product-collection` | write(auto) | `POST /designer/products/{id}/link-to-collection` | assign product to a collection. | `designer-link-product-to-collection` |
|
|
162
|
+
| `makea-cli designer submit production-order` | write(ask) | `POST /designer/production_orders/{production_order_id}/submit` | submit a 大货 order. This is the commitment, so --dry-run and confirm with the user first. | — |
|
|
163
|
+
| `makea-cli designer submit sampling-order` | write(ask) | `POST /designer/sampling_orders/{sampling_order_id}/confirm` | confirm a sample order so the supplier starts. Run --dry-run first and read it back to the user. | — |
|
|
158
164
|
| `makea-cli designer update action` | write(auto) | `POST /designer/actions/{action_id}/{acknowledge\|snooze\|dismiss}` | respond to one of the pending actions from `designer list action`. An action is closed by the fact it asks for, not b… | — |
|
|
159
165
|
| `makea-cli designer update note` | write(ask) | `POST /designer/products/{product_id}/notes/{note_id}/{accept\|reject}` | decide on a task the assistant proposed on a product. | — |
|
|
160
166
|
| `makea-cli designer update product` | write(auto) | `POST /designer/product/update_basic_info` | patch fields on a product. --cover-image / --back-image take a document id and are what the assistant's set_product_i… | — |
|
|
167
|
+
| `makea-cli designer update production-order` | write(ask) | `POST /designer/production_orders/{production_order_id}/complete` | POST /designer/production_orders/{id}/complete, or PUT the order to re-pick which supplier links it covers (--links-f… | — |
|
|
161
168
|
| `makea-cli designer update proposal` | write(ask) | `POST /designer/beeboo_proposals/approve` | apply one spec change the assistant proposed. This edits the pack, so read the proposal first. | — |
|
|
169
|
+
| `makea-cli designer update sampling-order` | write(never) | `POST /designer/sampling_orders/{sampling_order_id}/cancel` | cancel a sample order. Graded `never`: an agent must not do this on its own, a person runs it. | — |
|
|
170
|
+
| `makea-cli designer update shipping-order` | write(ask) | `POST /designer/shipping_orders/{shipping_order_id}/confirm_receive` | say the goods arrived. The backend accepts this only while the shipment is IN_TRANSIT. | — |
|
|
162
171
|
| `makea-cli designer update suggested-action` | write(ask) | `POST /designer/channels/{channel_id}/suggested_actions/bulk_accept` | accept several of a channel's suggested actions in one write. --action takes note_id:action_id and repeats. | — |
|
|
163
172
|
| `makea-cli designer update task` | write(auto) | `POST /designer/tasks/{complete\|dismiss}` | close one of the tasks from `designer list task`. Complete means you did it; dismiss means it does not apply. | — |
|
|
164
173
|
| `makea-cli designer upload document` | write(auto) | `POST /designer/document/upload` | upload a file for the current designer token; optional product_id (omit to start a new draft id server-side). | `designer-upload-document` |
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.2"
|
|
@@ -1312,3 +1312,49 @@ def designer_list_actions(*, product_id: str | None = None) -> Any:
|
|
|
1312
1312
|
def supplier_list_tasks() -> Any:
|
|
1313
1313
|
"""GET /supplier/tasks — the factory's onboarding and per-order to-dos."""
|
|
1314
1314
|
return get_json("/supplier/tasks")
|
|
1315
|
+
|
|
1316
|
+
|
|
1317
|
+
# --- order lifecycle (designer). Tiered on purpose: see the commands' approval
|
|
1318
|
+
# --- grades -- submit/confirm ask a person, cancel/delete are never automatic,
|
|
1319
|
+
# --- and nothing here moves money. Payments and subscriptions are deliberately
|
|
1320
|
+
# --- absent from the CLI: an agent should not have a path to a charge.
|
|
1321
|
+
|
|
1322
|
+
|
|
1323
|
+
def designer_confirm_sampling_order(sampling_order_id: str) -> Any:
|
|
1324
|
+
"""POST /designer/sampling_orders/{id}/confirm — the brand confirms the sample order."""
|
|
1325
|
+
return post_json(f"/designer/sampling_orders/{sampling_order_id}/confirm", {})
|
|
1326
|
+
|
|
1327
|
+
|
|
1328
|
+
def designer_cancel_sampling_order(sampling_order_id: str) -> Any:
|
|
1329
|
+
"""POST /designer/sampling_orders/{id}/cancel — cancel it."""
|
|
1330
|
+
return post_json(f"/designer/sampling_orders/{sampling_order_id}/cancel", {})
|
|
1331
|
+
|
|
1332
|
+
|
|
1333
|
+
def designer_delete_sampling_order(sampling_order_id: str) -> Any:
|
|
1334
|
+
"""DELETE /designer/sampling_orders/{id} — DRAFT only; the backend refuses anything else."""
|
|
1335
|
+
return delete_json(f"/designer/sampling_orders/{sampling_order_id}")
|
|
1336
|
+
|
|
1337
|
+
|
|
1338
|
+
def designer_submit_production_order(production_order_id: str) -> Any:
|
|
1339
|
+
"""POST /designer/production_orders/{id}/submit — hand the bulk order to the supplier."""
|
|
1340
|
+
return post_json(f"/designer/production_orders/{production_order_id}/submit", {})
|
|
1341
|
+
|
|
1342
|
+
|
|
1343
|
+
def designer_complete_production_order(production_order_id: str) -> Any:
|
|
1344
|
+
"""POST /designer/production_orders/{id}/complete — the brand marks the bulk order done."""
|
|
1345
|
+
return post_json(f"/designer/production_orders/{production_order_id}/complete", {})
|
|
1346
|
+
|
|
1347
|
+
|
|
1348
|
+
def designer_update_production_order(production_order_id: str, body: dict[str, Any]) -> Any:
|
|
1349
|
+
"""PUT /designer/production_orders/{id} — re-pick which supplier links the order covers."""
|
|
1350
|
+
return put_json(f"/designer/production_orders/{production_order_id}", body, timeout=120.0)
|
|
1351
|
+
|
|
1352
|
+
|
|
1353
|
+
def designer_confirm_receive_shipping_order(shipping_order_id: str) -> Any:
|
|
1354
|
+
"""POST /designer/shipping_orders/{id}/confirm_receive — IN_TRANSIT only."""
|
|
1355
|
+
return post_json(f"/designer/shipping_orders/{shipping_order_id}/confirm_receive", {})
|
|
1356
|
+
|
|
1357
|
+
|
|
1358
|
+
def designer_cancel_shipping_order(shipping_order_id: str) -> Any:
|
|
1359
|
+
"""POST /designer/shipping_orders/{id}/cancel — unlinks every production order, then deletes it."""
|
|
1360
|
+
return post_json(f"/designer/shipping_orders/{shipping_order_id}/cancel", {})
|
|
@@ -562,6 +562,197 @@ def register(app: typer.Typer) -> None:
|
|
|
562
562
|
body["channel_key"] = channel
|
|
563
563
|
print_json_or_exit(console, lambda: api_client.designer_add_product_note(product_id, body))
|
|
564
564
|
|
|
565
|
+
# --- order lifecycle ---------------------------------------------------------
|
|
566
|
+
#
|
|
567
|
+
# The `approval` grade on each of these is advice to a harness, not a gate in
|
|
568
|
+
# the CLI: a person in a terminal just runs the command. `ask` means an agent
|
|
569
|
+
# must put it in front of someone; `never` means an agent must not run it at
|
|
570
|
+
# all, which is why cancel and delete are their own commands rather than flags
|
|
571
|
+
# on an `ask` one -- the grade is per command, so mixing them would launder a
|
|
572
|
+
# destructive action through a milder grade.
|
|
573
|
+
#
|
|
574
|
+
# Payments and subscriptions are deliberately absent. They exist on the
|
|
575
|
+
# backend; giving them a CLI surface would put a charge one command away from
|
|
576
|
+
# an agent, and no approval grade makes that a good trade.
|
|
577
|
+
|
|
578
|
+
@command(
|
|
579
|
+
app,
|
|
580
|
+
"designer",
|
|
581
|
+
"submit",
|
|
582
|
+
"sampling-order",
|
|
583
|
+
api="POST /designer/sampling_orders/{sampling_order_id}/confirm",
|
|
584
|
+
side_effects=[
|
|
585
|
+
"confirms the sample order to the supplier",
|
|
586
|
+
"starts the supplier's clock on it",
|
|
587
|
+
],
|
|
588
|
+
approval="ask",
|
|
589
|
+
help=(
|
|
590
|
+
"POST /designer/sampling_orders/{id}/confirm — confirm a sample order so the "
|
|
591
|
+
"supplier starts. Run --dry-run first and read it back to the user."
|
|
592
|
+
),
|
|
593
|
+
)
|
|
594
|
+
def submit_sampling_order(
|
|
595
|
+
sampling_order_id: str = typer.Argument(..., help="Sampling order id."),
|
|
596
|
+
) -> None:
|
|
597
|
+
"""Confirm a sampling order."""
|
|
598
|
+
print_json_or_exit(
|
|
599
|
+
console, lambda: api_client.designer_confirm_sampling_order(sampling_order_id)
|
|
600
|
+
)
|
|
601
|
+
|
|
602
|
+
@command(
|
|
603
|
+
app,
|
|
604
|
+
"designer",
|
|
605
|
+
"update",
|
|
606
|
+
"sampling-order",
|
|
607
|
+
api="POST /designer/sampling_orders/{sampling_order_id}/cancel",
|
|
608
|
+
side_effects=["cancels the order", "tells the supplier to stop"],
|
|
609
|
+
approval="never",
|
|
610
|
+
help=(
|
|
611
|
+
"POST /designer/sampling_orders/{id}/cancel — cancel a sample order. Graded "
|
|
612
|
+
"`never`: an agent must not do this on its own, a person runs it."
|
|
613
|
+
),
|
|
614
|
+
)
|
|
615
|
+
def update_sampling_order(
|
|
616
|
+
sampling_order_id: str = typer.Argument(..., help="Sampling order id."),
|
|
617
|
+
cancel: bool = typer.Option(
|
|
618
|
+
..., "--cancel", help="The only operation here; explicit on purpose."
|
|
619
|
+
),
|
|
620
|
+
) -> None:
|
|
621
|
+
"""Cancel a sampling order."""
|
|
622
|
+
if not cancel:
|
|
623
|
+
raise typer.BadParameter("--cancel is required")
|
|
624
|
+
print_json_or_exit(
|
|
625
|
+
console, lambda: api_client.designer_cancel_sampling_order(sampling_order_id)
|
|
626
|
+
)
|
|
627
|
+
|
|
628
|
+
@command(
|
|
629
|
+
app,
|
|
630
|
+
"designer",
|
|
631
|
+
"delete",
|
|
632
|
+
"sampling-order",
|
|
633
|
+
api="DELETE /designer/sampling_orders/{sampling_order_id}",
|
|
634
|
+
side_effects=["removes the order row; the backend allows this on DRAFT only"],
|
|
635
|
+
approval="never",
|
|
636
|
+
help=(
|
|
637
|
+
"DELETE /designer/sampling_orders/{id} — delete a DRAFT sample order. The backend "
|
|
638
|
+
"refuses any other status. Graded `never`."
|
|
639
|
+
),
|
|
640
|
+
)
|
|
641
|
+
def delete_sampling_order(
|
|
642
|
+
sampling_order_id: str = typer.Argument(..., help="Sampling order id (DRAFT)."),
|
|
643
|
+
) -> None:
|
|
644
|
+
"""Delete a draft sampling order."""
|
|
645
|
+
print_json_or_exit(
|
|
646
|
+
console, lambda: api_client.designer_delete_sampling_order(sampling_order_id)
|
|
647
|
+
)
|
|
648
|
+
|
|
649
|
+
@command(
|
|
650
|
+
app,
|
|
651
|
+
"designer",
|
|
652
|
+
"submit",
|
|
653
|
+
"production-order",
|
|
654
|
+
api="POST /designer/production_orders/{production_order_id}/submit",
|
|
655
|
+
side_effects=["hands the bulk order to the supplier", "creates its charges"],
|
|
656
|
+
approval="ask",
|
|
657
|
+
help=(
|
|
658
|
+
"POST /designer/production_orders/{id}/submit — submit a 大货 order. This is the "
|
|
659
|
+
"commitment, so --dry-run and confirm with the user first."
|
|
660
|
+
),
|
|
661
|
+
)
|
|
662
|
+
def submit_production_order(
|
|
663
|
+
production_order_id: str = typer.Argument(..., help="Production order id."),
|
|
664
|
+
) -> None:
|
|
665
|
+
"""Submit a production order."""
|
|
666
|
+
print_json_or_exit(
|
|
667
|
+
console, lambda: api_client.designer_submit_production_order(production_order_id)
|
|
668
|
+
)
|
|
669
|
+
|
|
670
|
+
@command(
|
|
671
|
+
app,
|
|
672
|
+
"designer",
|
|
673
|
+
"update",
|
|
674
|
+
"production-order",
|
|
675
|
+
api="POST /designer/production_orders/{production_order_id}/complete",
|
|
676
|
+
side_effects=["completing closes the order for both sides"],
|
|
677
|
+
approval="ask",
|
|
678
|
+
help=(
|
|
679
|
+
"POST /designer/production_orders/{id}/complete, or PUT the order to re-pick which "
|
|
680
|
+
"supplier links it covers (--links-file, same body shape as creating it)."
|
|
681
|
+
),
|
|
682
|
+
)
|
|
683
|
+
def update_production_order(
|
|
684
|
+
production_order_id: str = typer.Argument(..., help="Production order id."),
|
|
685
|
+
complete: bool = typer.Option(False, "--complete", help="Mark the order finished."),
|
|
686
|
+
links_file: Path | None = typer.Option(
|
|
687
|
+
None,
|
|
688
|
+
"--links-file",
|
|
689
|
+
exists=True,
|
|
690
|
+
dir_okay=False,
|
|
691
|
+
help="JSON with selected_links, to change what the order covers.",
|
|
692
|
+
),
|
|
693
|
+
) -> None:
|
|
694
|
+
"""Complete a production order, or change the links it covers."""
|
|
695
|
+
if complete == (links_file is not None):
|
|
696
|
+
raise typer.BadParameter("pass exactly one of --complete / --links-file")
|
|
697
|
+
if complete:
|
|
698
|
+
run = lambda: api_client.designer_complete_production_order(production_order_id) # noqa: E731
|
|
699
|
+
else:
|
|
700
|
+
body = _json_object(None, links_file, "links-file")
|
|
701
|
+
run = lambda: api_client.designer_update_production_order(production_order_id, body) # noqa: E731
|
|
702
|
+
print_json_or_exit(console, run)
|
|
703
|
+
|
|
704
|
+
@command(
|
|
705
|
+
app,
|
|
706
|
+
"designer",
|
|
707
|
+
"update",
|
|
708
|
+
"shipping-order",
|
|
709
|
+
api="POST /designer/shipping_orders/{shipping_order_id}/confirm_receive",
|
|
710
|
+
side_effects=["marks the shipment received, which closes it"],
|
|
711
|
+
approval="ask",
|
|
712
|
+
help=(
|
|
713
|
+
"POST /designer/shipping_orders/{id}/confirm_receive — say the goods arrived. The "
|
|
714
|
+
"backend accepts this only while the shipment is IN_TRANSIT."
|
|
715
|
+
),
|
|
716
|
+
)
|
|
717
|
+
def update_shipping_order(
|
|
718
|
+
shipping_order_id: str = typer.Argument(..., help="Shipping order id."),
|
|
719
|
+
confirm_receive: bool = typer.Option(
|
|
720
|
+
..., "--confirm-receive", help="The only operation here; explicit on purpose."
|
|
721
|
+
),
|
|
722
|
+
) -> None:
|
|
723
|
+
"""Confirm receipt of a shipment."""
|
|
724
|
+
if not confirm_receive:
|
|
725
|
+
raise typer.BadParameter("--confirm-receive is required")
|
|
726
|
+
print_json_or_exit(
|
|
727
|
+
console,
|
|
728
|
+
lambda: api_client.designer_confirm_receive_shipping_order(shipping_order_id),
|
|
729
|
+
)
|
|
730
|
+
|
|
731
|
+
@command(
|
|
732
|
+
app,
|
|
733
|
+
"designer",
|
|
734
|
+
"delete",
|
|
735
|
+
"shipping-order",
|
|
736
|
+
api="POST /designer/shipping_orders/{shipping_order_id}/cancel",
|
|
737
|
+
side_effects=[
|
|
738
|
+
"unlinks every production order on the shipment",
|
|
739
|
+
"deletes the shipment itself",
|
|
740
|
+
],
|
|
741
|
+
approval="never",
|
|
742
|
+
help=(
|
|
743
|
+
"POST /designer/shipping_orders/{id}/cancel — the endpoint is called cancel but it "
|
|
744
|
+
"unlinks every production order and removes the shipment, so it is a delete here. "
|
|
745
|
+
"Graded `never`."
|
|
746
|
+
),
|
|
747
|
+
)
|
|
748
|
+
def delete_shipping_order(
|
|
749
|
+
shipping_order_id: str = typer.Argument(..., help="Shipping order id."),
|
|
750
|
+
) -> None:
|
|
751
|
+
"""Cancel (and remove) a shipping order."""
|
|
752
|
+
print_json_or_exit(
|
|
753
|
+
console, lambda: api_client.designer_cancel_shipping_order(shipping_order_id)
|
|
754
|
+
)
|
|
755
|
+
|
|
565
756
|
@command(
|
|
566
757
|
app,
|
|
567
758
|
"designer",
|
|
@@ -4817,6 +4817,73 @@
|
|
|
4817
4817
|
}
|
|
4818
4818
|
]
|
|
4819
4819
|
},
|
|
4820
|
+
{
|
|
4821
|
+
"name": "designer delete sampling-order",
|
|
4822
|
+
"persona": "designer",
|
|
4823
|
+
"verb": "delete",
|
|
4824
|
+
"noun": "sampling-order",
|
|
4825
|
+
"access": "write",
|
|
4826
|
+
"api": "DELETE /designer/sampling_orders/{sampling_order_id}",
|
|
4827
|
+
"external": null,
|
|
4828
|
+
"side_effects": [
|
|
4829
|
+
"removes the order row; the backend allows this on DRAFT only"
|
|
4830
|
+
],
|
|
4831
|
+
"approval": "never",
|
|
4832
|
+
"reversible": null,
|
|
4833
|
+
"idempotent": null,
|
|
4834
|
+
"limit": null,
|
|
4835
|
+
"legacy": [],
|
|
4836
|
+
"help": "DELETE /designer/sampling_orders/{id} — delete a DRAFT sample order. The backend refuses any other status. Graded `never`.",
|
|
4837
|
+
"function": "makea_cli.commands.brand.cmd.register.<locals>.delete_sampling_order",
|
|
4838
|
+
"flags": [
|
|
4839
|
+
{
|
|
4840
|
+
"name": "sampling_order_id",
|
|
4841
|
+
"kind": "argument",
|
|
4842
|
+
"opts": [
|
|
4843
|
+
"sampling_order_id"
|
|
4844
|
+
],
|
|
4845
|
+
"type": "text",
|
|
4846
|
+
"required": true,
|
|
4847
|
+
"multiple": false,
|
|
4848
|
+
"default": null,
|
|
4849
|
+
"help": "Sampling order id (DRAFT)."
|
|
4850
|
+
}
|
|
4851
|
+
]
|
|
4852
|
+
},
|
|
4853
|
+
{
|
|
4854
|
+
"name": "designer delete shipping-order",
|
|
4855
|
+
"persona": "designer",
|
|
4856
|
+
"verb": "delete",
|
|
4857
|
+
"noun": "shipping-order",
|
|
4858
|
+
"access": "write",
|
|
4859
|
+
"api": "POST /designer/shipping_orders/{shipping_order_id}/cancel",
|
|
4860
|
+
"external": null,
|
|
4861
|
+
"side_effects": [
|
|
4862
|
+
"unlinks every production order on the shipment",
|
|
4863
|
+
"deletes the shipment itself"
|
|
4864
|
+
],
|
|
4865
|
+
"approval": "never",
|
|
4866
|
+
"reversible": null,
|
|
4867
|
+
"idempotent": null,
|
|
4868
|
+
"limit": null,
|
|
4869
|
+
"legacy": [],
|
|
4870
|
+
"help": "POST /designer/shipping_orders/{id}/cancel — the endpoint is called cancel but it unlinks every production order and removes the shipment, so it is a delete here. Graded `never`.",
|
|
4871
|
+
"function": "makea_cli.commands.brand.cmd.register.<locals>.delete_shipping_order",
|
|
4872
|
+
"flags": [
|
|
4873
|
+
{
|
|
4874
|
+
"name": "shipping_order_id",
|
|
4875
|
+
"kind": "argument",
|
|
4876
|
+
"opts": [
|
|
4877
|
+
"shipping_order_id"
|
|
4878
|
+
],
|
|
4879
|
+
"type": "text",
|
|
4880
|
+
"required": true,
|
|
4881
|
+
"multiple": false,
|
|
4882
|
+
"default": null,
|
|
4883
|
+
"help": "Shipping order id."
|
|
4884
|
+
}
|
|
4885
|
+
]
|
|
4886
|
+
},
|
|
4820
4887
|
{
|
|
4821
4888
|
"name": "designer download document",
|
|
4822
4889
|
"persona": "designer",
|
|
@@ -5532,6 +5599,74 @@
|
|
|
5532
5599
|
}
|
|
5533
5600
|
]
|
|
5534
5601
|
},
|
|
5602
|
+
{
|
|
5603
|
+
"name": "designer submit production-order",
|
|
5604
|
+
"persona": "designer",
|
|
5605
|
+
"verb": "submit",
|
|
5606
|
+
"noun": "production-order",
|
|
5607
|
+
"access": "write",
|
|
5608
|
+
"api": "POST /designer/production_orders/{production_order_id}/submit",
|
|
5609
|
+
"external": null,
|
|
5610
|
+
"side_effects": [
|
|
5611
|
+
"hands the bulk order to the supplier",
|
|
5612
|
+
"creates its charges"
|
|
5613
|
+
],
|
|
5614
|
+
"approval": "ask",
|
|
5615
|
+
"reversible": null,
|
|
5616
|
+
"idempotent": null,
|
|
5617
|
+
"limit": null,
|
|
5618
|
+
"legacy": [],
|
|
5619
|
+
"help": "POST /designer/production_orders/{id}/submit — submit a 大货 order. This is the commitment, so --dry-run and confirm with the user first.",
|
|
5620
|
+
"function": "makea_cli.commands.brand.cmd.register.<locals>.submit_production_order",
|
|
5621
|
+
"flags": [
|
|
5622
|
+
{
|
|
5623
|
+
"name": "production_order_id",
|
|
5624
|
+
"kind": "argument",
|
|
5625
|
+
"opts": [
|
|
5626
|
+
"production_order_id"
|
|
5627
|
+
],
|
|
5628
|
+
"type": "text",
|
|
5629
|
+
"required": true,
|
|
5630
|
+
"multiple": false,
|
|
5631
|
+
"default": null,
|
|
5632
|
+
"help": "Production order id."
|
|
5633
|
+
}
|
|
5634
|
+
]
|
|
5635
|
+
},
|
|
5636
|
+
{
|
|
5637
|
+
"name": "designer submit sampling-order",
|
|
5638
|
+
"persona": "designer",
|
|
5639
|
+
"verb": "submit",
|
|
5640
|
+
"noun": "sampling-order",
|
|
5641
|
+
"access": "write",
|
|
5642
|
+
"api": "POST /designer/sampling_orders/{sampling_order_id}/confirm",
|
|
5643
|
+
"external": null,
|
|
5644
|
+
"side_effects": [
|
|
5645
|
+
"confirms the sample order to the supplier",
|
|
5646
|
+
"starts the supplier's clock on it"
|
|
5647
|
+
],
|
|
5648
|
+
"approval": "ask",
|
|
5649
|
+
"reversible": null,
|
|
5650
|
+
"idempotent": null,
|
|
5651
|
+
"limit": null,
|
|
5652
|
+
"legacy": [],
|
|
5653
|
+
"help": "POST /designer/sampling_orders/{id}/confirm — confirm a sample order so the supplier starts. Run --dry-run first and read it back to the user.",
|
|
5654
|
+
"function": "makea_cli.commands.brand.cmd.register.<locals>.submit_sampling_order",
|
|
5655
|
+
"flags": [
|
|
5656
|
+
{
|
|
5657
|
+
"name": "sampling_order_id",
|
|
5658
|
+
"kind": "argument",
|
|
5659
|
+
"opts": [
|
|
5660
|
+
"sampling_order_id"
|
|
5661
|
+
],
|
|
5662
|
+
"type": "text",
|
|
5663
|
+
"required": true,
|
|
5664
|
+
"multiple": false,
|
|
5665
|
+
"default": null,
|
|
5666
|
+
"help": "Sampling order id."
|
|
5667
|
+
}
|
|
5668
|
+
]
|
|
5669
|
+
},
|
|
5535
5670
|
{
|
|
5536
5671
|
"name": "designer update action",
|
|
5537
5672
|
"persona": "designer",
|
|
@@ -5750,6 +5885,63 @@
|
|
|
5750
5885
|
}
|
|
5751
5886
|
]
|
|
5752
5887
|
},
|
|
5888
|
+
{
|
|
5889
|
+
"name": "designer update production-order",
|
|
5890
|
+
"persona": "designer",
|
|
5891
|
+
"verb": "update",
|
|
5892
|
+
"noun": "production-order",
|
|
5893
|
+
"access": "write",
|
|
5894
|
+
"api": "POST /designer/production_orders/{production_order_id}/complete",
|
|
5895
|
+
"external": null,
|
|
5896
|
+
"side_effects": [
|
|
5897
|
+
"completing closes the order for both sides"
|
|
5898
|
+
],
|
|
5899
|
+
"approval": "ask",
|
|
5900
|
+
"reversible": null,
|
|
5901
|
+
"idempotent": null,
|
|
5902
|
+
"limit": null,
|
|
5903
|
+
"legacy": [],
|
|
5904
|
+
"help": "POST /designer/production_orders/{id}/complete, or PUT the order to re-pick which supplier links it covers (--links-file, same body shape as creating it).",
|
|
5905
|
+
"function": "makea_cli.commands.brand.cmd.register.<locals>.update_production_order",
|
|
5906
|
+
"flags": [
|
|
5907
|
+
{
|
|
5908
|
+
"name": "production_order_id",
|
|
5909
|
+
"kind": "argument",
|
|
5910
|
+
"opts": [
|
|
5911
|
+
"production_order_id"
|
|
5912
|
+
],
|
|
5913
|
+
"type": "text",
|
|
5914
|
+
"required": true,
|
|
5915
|
+
"multiple": false,
|
|
5916
|
+
"default": null,
|
|
5917
|
+
"help": "Production order id."
|
|
5918
|
+
},
|
|
5919
|
+
{
|
|
5920
|
+
"name": "complete",
|
|
5921
|
+
"kind": "option",
|
|
5922
|
+
"opts": [
|
|
5923
|
+
"--complete"
|
|
5924
|
+
],
|
|
5925
|
+
"type": "flag",
|
|
5926
|
+
"required": false,
|
|
5927
|
+
"multiple": false,
|
|
5928
|
+
"default": false,
|
|
5929
|
+
"help": "Mark the order finished."
|
|
5930
|
+
},
|
|
5931
|
+
{
|
|
5932
|
+
"name": "links_file",
|
|
5933
|
+
"kind": "option",
|
|
5934
|
+
"opts": [
|
|
5935
|
+
"--links-file"
|
|
5936
|
+
],
|
|
5937
|
+
"type": "file",
|
|
5938
|
+
"required": false,
|
|
5939
|
+
"multiple": false,
|
|
5940
|
+
"default": null,
|
|
5941
|
+
"help": "JSON with selected_links, to change what the order covers."
|
|
5942
|
+
}
|
|
5943
|
+
]
|
|
5944
|
+
},
|
|
5753
5945
|
{
|
|
5754
5946
|
"name": "designer update proposal",
|
|
5755
5947
|
"persona": "designer",
|
|
@@ -5796,6 +5988,97 @@
|
|
|
5796
5988
|
}
|
|
5797
5989
|
]
|
|
5798
5990
|
},
|
|
5991
|
+
{
|
|
5992
|
+
"name": "designer update sampling-order",
|
|
5993
|
+
"persona": "designer",
|
|
5994
|
+
"verb": "update",
|
|
5995
|
+
"noun": "sampling-order",
|
|
5996
|
+
"access": "write",
|
|
5997
|
+
"api": "POST /designer/sampling_orders/{sampling_order_id}/cancel",
|
|
5998
|
+
"external": null,
|
|
5999
|
+
"side_effects": [
|
|
6000
|
+
"cancels the order",
|
|
6001
|
+
"tells the supplier to stop"
|
|
6002
|
+
],
|
|
6003
|
+
"approval": "never",
|
|
6004
|
+
"reversible": null,
|
|
6005
|
+
"idempotent": null,
|
|
6006
|
+
"limit": null,
|
|
6007
|
+
"legacy": [],
|
|
6008
|
+
"help": "POST /designer/sampling_orders/{id}/cancel — cancel a sample order. Graded `never`: an agent must not do this on its own, a person runs it.",
|
|
6009
|
+
"function": "makea_cli.commands.brand.cmd.register.<locals>.update_sampling_order",
|
|
6010
|
+
"flags": [
|
|
6011
|
+
{
|
|
6012
|
+
"name": "sampling_order_id",
|
|
6013
|
+
"kind": "argument",
|
|
6014
|
+
"opts": [
|
|
6015
|
+
"sampling_order_id"
|
|
6016
|
+
],
|
|
6017
|
+
"type": "text",
|
|
6018
|
+
"required": true,
|
|
6019
|
+
"multiple": false,
|
|
6020
|
+
"default": null,
|
|
6021
|
+
"help": "Sampling order id."
|
|
6022
|
+
},
|
|
6023
|
+
{
|
|
6024
|
+
"name": "cancel",
|
|
6025
|
+
"kind": "option",
|
|
6026
|
+
"opts": [
|
|
6027
|
+
"--cancel"
|
|
6028
|
+
],
|
|
6029
|
+
"type": "flag",
|
|
6030
|
+
"required": true,
|
|
6031
|
+
"multiple": false,
|
|
6032
|
+
"default": null,
|
|
6033
|
+
"help": "The only operation here; explicit on purpose."
|
|
6034
|
+
}
|
|
6035
|
+
]
|
|
6036
|
+
},
|
|
6037
|
+
{
|
|
6038
|
+
"name": "designer update shipping-order",
|
|
6039
|
+
"persona": "designer",
|
|
6040
|
+
"verb": "update",
|
|
6041
|
+
"noun": "shipping-order",
|
|
6042
|
+
"access": "write",
|
|
6043
|
+
"api": "POST /designer/shipping_orders/{shipping_order_id}/confirm_receive",
|
|
6044
|
+
"external": null,
|
|
6045
|
+
"side_effects": [
|
|
6046
|
+
"marks the shipment received, which closes it"
|
|
6047
|
+
],
|
|
6048
|
+
"approval": "ask",
|
|
6049
|
+
"reversible": null,
|
|
6050
|
+
"idempotent": null,
|
|
6051
|
+
"limit": null,
|
|
6052
|
+
"legacy": [],
|
|
6053
|
+
"help": "POST /designer/shipping_orders/{id}/confirm_receive — say the goods arrived. The backend accepts this only while the shipment is IN_TRANSIT.",
|
|
6054
|
+
"function": "makea_cli.commands.brand.cmd.register.<locals>.update_shipping_order",
|
|
6055
|
+
"flags": [
|
|
6056
|
+
{
|
|
6057
|
+
"name": "shipping_order_id",
|
|
6058
|
+
"kind": "argument",
|
|
6059
|
+
"opts": [
|
|
6060
|
+
"shipping_order_id"
|
|
6061
|
+
],
|
|
6062
|
+
"type": "text",
|
|
6063
|
+
"required": true,
|
|
6064
|
+
"multiple": false,
|
|
6065
|
+
"default": null,
|
|
6066
|
+
"help": "Shipping order id."
|
|
6067
|
+
},
|
|
6068
|
+
{
|
|
6069
|
+
"name": "confirm_receive",
|
|
6070
|
+
"kind": "option",
|
|
6071
|
+
"opts": [
|
|
6072
|
+
"--confirm-receive"
|
|
6073
|
+
],
|
|
6074
|
+
"type": "flag",
|
|
6075
|
+
"required": true,
|
|
6076
|
+
"multiple": false,
|
|
6077
|
+
"default": null,
|
|
6078
|
+
"help": "The only operation here; explicit on purpose."
|
|
6079
|
+
}
|
|
6080
|
+
]
|
|
6081
|
+
},
|
|
5799
6082
|
{
|
|
5800
6083
|
"name": "designer update suggested-action",
|
|
5801
6084
|
"persona": "designer",
|
|
@@ -323,3 +323,89 @@ class SupplierLifecycleTests(_PersonaCase):
|
|
|
323
323
|
self.run_cli("supplier", "update", "action", "a-1", "--snooze", "2")
|
|
324
324
|
self.assertEqual(self.rec.last["path"], "/supplier/actions/a-1/snooze")
|
|
325
325
|
self.assertEqual(self.rec.last["body"], {"days": 2})
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
class OrderLifecycleTests(_PersonaCase):
|
|
329
|
+
"""The tiered order commands, and the grades that make the tiering real."""
|
|
330
|
+
|
|
331
|
+
persona = "designer"
|
|
332
|
+
|
|
333
|
+
def test_confirm_and_submit_endpoints(self):
|
|
334
|
+
self.run_cli("designer", "submit", "sampling-order", "so-1")
|
|
335
|
+
self.assertEqual(self.rec.last["path"], "/designer/sampling_orders/so-1/confirm")
|
|
336
|
+
self.run_cli("designer", "submit", "production-order", "po-1")
|
|
337
|
+
self.assertEqual(self.rec.last["path"], "/designer/production_orders/po-1/submit")
|
|
338
|
+
|
|
339
|
+
def test_cancel_and_delete_are_separate_commands(self):
|
|
340
|
+
self.run_cli("designer", "update", "sampling-order", "so-1", "--cancel")
|
|
341
|
+
self.assertEqual(self.rec.last["path"], "/designer/sampling_orders/so-1/cancel")
|
|
342
|
+
self.run_cli("designer", "delete", "sampling-order", "so-2")
|
|
343
|
+
self.assertEqual(self.rec.last["method"], "DELETE")
|
|
344
|
+
self.assertEqual(self.rec.last["path"], "/designer/sampling_orders/so-2")
|
|
345
|
+
|
|
346
|
+
def test_production_order_update_takes_one_intent(self):
|
|
347
|
+
result = self.runner.invoke(self.cli, ["designer", "update", "production-order", "po-1"])
|
|
348
|
+
self.assertEqual(result.exit_code, 2)
|
|
349
|
+
self.run_cli("designer", "update", "production-order", "po-1", "--complete")
|
|
350
|
+
self.assertEqual(self.rec.last["path"], "/designer/production_orders/po-1/complete")
|
|
351
|
+
|
|
352
|
+
def test_production_order_links_file_is_a_put(self):
|
|
353
|
+
with self.runner.isolated_filesystem():
|
|
354
|
+
with open("links.json", "w") as f:
|
|
355
|
+
json.dump({"selected_links": [{"link_id": "lk-1"}]}, f)
|
|
356
|
+
self.run_cli(
|
|
357
|
+
"designer", "update", "production-order", "po-1", "--links-file", "links.json"
|
|
358
|
+
)
|
|
359
|
+
self.assertEqual(self.rec.last["method"], "PUT")
|
|
360
|
+
self.assertEqual(self.rec.last["body"], {"selected_links": [{"link_id": "lk-1"}]})
|
|
361
|
+
|
|
362
|
+
def test_shipping_receive_and_removal(self):
|
|
363
|
+
self.run_cli("designer", "update", "shipping-order", "sh-1", "--confirm-receive")
|
|
364
|
+
self.assertEqual(self.rec.last["path"], "/designer/shipping_orders/sh-1/confirm_receive")
|
|
365
|
+
self.run_cli("designer", "delete", "shipping-order", "sh-1")
|
|
366
|
+
self.assertEqual(self.rec.last["path"], "/designer/shipping_orders/sh-1/cancel")
|
|
367
|
+
|
|
368
|
+
def test_destructive_commands_are_graded_never(self):
|
|
369
|
+
from makea_cli import registry
|
|
370
|
+
|
|
371
|
+
for persona, verb, noun in (
|
|
372
|
+
("designer", "update", "sampling-order"),
|
|
373
|
+
("designer", "delete", "sampling-order"),
|
|
374
|
+
("designer", "delete", "shipping-order"),
|
|
375
|
+
):
|
|
376
|
+
spec = registry.spec_for(persona, verb, noun)
|
|
377
|
+
self.assertEqual(spec.approval, "never", spec.name)
|
|
378
|
+
|
|
379
|
+
def test_commitments_are_graded_ask(self):
|
|
380
|
+
from makea_cli import registry
|
|
381
|
+
|
|
382
|
+
for persona, verb, noun in (
|
|
383
|
+
("designer", "submit", "sampling-order"),
|
|
384
|
+
("designer", "submit", "production-order"),
|
|
385
|
+
("designer", "update", "production-order"),
|
|
386
|
+
("designer", "update", "shipping-order"),
|
|
387
|
+
):
|
|
388
|
+
spec = registry.spec_for(persona, verb, noun)
|
|
389
|
+
self.assertEqual(spec.approval, "ask", spec.name)
|
|
390
|
+
|
|
391
|
+
def test_the_cli_itself_does_not_block_a_never_command(self):
|
|
392
|
+
"""The grade is advice to a harness; a person in a terminal still runs it."""
|
|
393
|
+
self.run_cli("designer", "delete", "shipping-order", "sh-9")
|
|
394
|
+
self.assertEqual(len(self.rec.calls), 1)
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
class NoMoneyPathTests(unittest.TestCase):
|
|
398
|
+
"""No CLI command may reach a payment or subscription endpoint."""
|
|
399
|
+
|
|
400
|
+
def test_no_command_touches_money(self):
|
|
401
|
+
from makea_cli import registry
|
|
402
|
+
from makea_cli.main import build_cli
|
|
403
|
+
|
|
404
|
+
build_cli()
|
|
405
|
+
forbidden = ("/payments", "/subscription", "/bank-transfers", "create-session", "checkout")
|
|
406
|
+
offenders = [
|
|
407
|
+
(s.name, s.api)
|
|
408
|
+
for s in registry.specs()
|
|
409
|
+
if not s.external and any(f in s.api for f in forbidden) and s.access == "write"
|
|
410
|
+
]
|
|
411
|
+
self.assertEqual(offenders, [], "a write command reaches money movement")
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.2.1"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|