makea-cli 0.2.16__py3-none-any.whl → 0.2.17__py3-none-any.whl

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/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.2.16"
1
+ __version__ = "0.2.17"
makea_cli/api_client.py CHANGED
@@ -766,6 +766,89 @@ def get_all_shipping_orders(
766
766
  return get_json("/admin/shipping_orders", params=params)
767
767
 
768
768
 
769
+ def admin_create_shipping_order(body: dict[str, Any]) -> Any:
770
+ """POST /admin/shipping_orders/create — a shipment for a brand's production/sampling orders."""
771
+ return post_json("/admin/shipping_orders/create", body)
772
+
773
+
774
+ def admin_get_shipping_order(shipping_order_id: str) -> Any:
775
+ """GET /admin/shipping_orders/{id}/details — the shipment with its orders filled in."""
776
+ return get_json(f"/admin/shipping_orders/{shipping_order_id}/details")
777
+
778
+
779
+ def admin_update_shipping_order_status(shipping_order_id: str, status: str) -> Any:
780
+ """PUT /admin/shipping_orders/{id}/status"""
781
+ return put_json(f"/admin/shipping_orders/{shipping_order_id}/status", {"status": status})
782
+
783
+
784
+ def admin_update_shipping_order_tracking(
785
+ shipping_order_id: str, tracking_info: dict[str, Any]
786
+ ) -> Any:
787
+ """POST /admin/shipping_orders/{id}/tracking"""
788
+ return post_json(
789
+ f"/admin/shipping_orders/{shipping_order_id}/tracking", {"tracking_info": tracking_info}
790
+ )
791
+
792
+
793
+ def admin_update_shipping_order_address(
794
+ shipping_order_id: str, shipping_address: dict[str, Any]
795
+ ) -> Any:
796
+ """PUT /admin/shipping_orders/{id}/shipping-address"""
797
+ return put_json(
798
+ f"/admin/shipping_orders/{shipping_order_id}/shipping-address",
799
+ {"shipping_address": shipping_address},
800
+ )
801
+
802
+
803
+ def admin_update_shipping_order_cargo(shipping_order_id: str, cargo: dict[str, Any]) -> Any:
804
+ """PUT /admin/shipping_orders/{id}/cargo — cartons and/or gross_weight_kg."""
805
+ return put_json(f"/admin/shipping_orders/{shipping_order_id}/cargo", cargo)
806
+
807
+
808
+ def admin_delete_shipping_order(shipping_order_id: str) -> Any:
809
+ """DELETE /admin/shipping_orders/{id}/delete — soft delete; unlinks its orders."""
810
+ return delete_json(f"/admin/shipping_orders/{shipping_order_id}/delete")
811
+
812
+
813
+ def admin_add_shipping_option(shipping_order_id: str, option: dict[str, Any]) -> Any:
814
+ """POST /admin/shipping_orders/{id}/shipping-options — one freight quote."""
815
+ return post_json(
816
+ f"/admin/shipping_orders/{shipping_order_id}/shipping-options",
817
+ {"shipping_option": option},
818
+ )
819
+
820
+
821
+ def admin_remove_shipping_option(shipping_order_id: str, shipping_option_id: str) -> Any:
822
+ """DELETE /admin/shipping_orders/{id}/shipping-options/{option_id}/remove"""
823
+ return delete_json(
824
+ f"/admin/shipping_orders/{shipping_order_id}/shipping-options/{shipping_option_id}/remove"
825
+ )
826
+
827
+
828
+ def admin_add_shipping_order_fee(shipping_order_id: str, fee: dict[str, Any]) -> Any:
829
+ """POST /admin/shipping_orders/{id}/fees/add"""
830
+ return post_json(f"/admin/shipping_orders/{shipping_order_id}/fees/add", fee)
831
+
832
+
833
+ def admin_remove_shipping_order_fee(shipping_order_id: str, fee_id: str) -> Any:
834
+ """DELETE /admin/shipping_orders/{id}/fees/{fee_id}/remove"""
835
+ return delete_json(f"/admin/shipping_orders/{shipping_order_id}/fees/{fee_id}/remove")
836
+
837
+
838
+ def admin_unlink_from_shipping_order(
839
+ shipping_order_id: str,
840
+ *,
841
+ production_order_id: str | None = None,
842
+ sampling_order_id: str | None = None,
843
+ ) -> Any:
844
+ """POST /admin/{production|sampling}_orders/{id}/unlink_from_shipping_order/{shipping_order_id}"""
845
+ if production_order_id:
846
+ path = f"/admin/production_orders/{production_order_id}"
847
+ else:
848
+ path = f"/admin/sampling_orders/{sampling_order_id}"
849
+ return post_json(f"{path}/unlink_from_shipping_order/{shipping_order_id}", {})
850
+
851
+
769
852
  def get_misc_payments_by_product(product_id: str) -> Any:
770
853
  """GET /admin/products/{product_id}/misc-payments — misc payments for one product."""
771
854
  return get_json(f"/admin/products/{product_id}/misc-payments")
@@ -16,6 +16,7 @@ from makea_cli.commands.orders.cmd import register as register_orders
16
16
  from makea_cli.commands.playbook.cmd import register as register_playbook
17
17
  from makea_cli.commands.product.cmd import register as register_product
18
18
  from makea_cli.commands.production.cmd import register as register_production
19
+ from makea_cli.commands.shipping.cmd import register as register_shipping
19
20
  from makea_cli.commands.supplier.cmd import register as register_supplier
20
21
 
21
22
 
@@ -27,6 +28,7 @@ def register_all(app: typer.Typer) -> None:
27
28
  register_orders(app)
28
29
  register_product(app)
29
30
  register_production(app)
31
+ register_shipping(app)
30
32
  register_designer(app)
31
33
  register_metrics(app)
32
34
  register_analytics(app)
File without changes
@@ -0,0 +1,422 @@
1
+ """Admin shipping orders: make one for a brand, then run it (status, tracking, quotes, fees)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ import typer
10
+ from rich.console import Console
11
+
12
+ from makea_cli import api_client
13
+ from makea_cli.commands._util import print_json_or_exit
14
+ from makea_cli.registry import command
15
+
16
+ console = Console()
17
+
18
+ SHIPPING_STATUSES = ("DRAFT", "SUBMITTED", "READY_TO_PAY", "PAID", "IN_TRANSIT", "DELIVERED")
19
+ SHIPPING_MODES = ("AIR", "SEA", "LAND", "TRUCK", "RAIL", "SEA_TRUCK")
20
+ FEE_TYPES = ("SHIPPING", "SERVICE", "HANDLING", "CUSTOMS", "INSURANCE", "TRANSACTION", "OTHER")
21
+
22
+
23
+ def _address(raw: str | None, path: Path | None) -> dict[str, Any] | None:
24
+ if raw is None and path is None:
25
+ return None
26
+ if raw is not None and path is not None:
27
+ raise typer.BadParameter("pass only one of --address / --address-file")
28
+ text = raw if raw is not None else path.read_text(encoding="utf-8") # type: ignore[union-attr]
29
+ try:
30
+ data = json.loads(text)
31
+ except json.JSONDecodeError as e:
32
+ raise typer.BadParameter(f"address: invalid JSON ({e})") from e
33
+ if not isinstance(data, dict):
34
+ raise typer.BadParameter("address: expected a JSON object")
35
+ return data
36
+
37
+
38
+ def _choice(value: str | None, allowed: tuple[str, ...], flag: str) -> str | None:
39
+ if value is None:
40
+ return None
41
+ v = value.strip().upper()
42
+ if v not in allowed:
43
+ raise typer.BadParameter(f"{flag} must be one of {', '.join(allowed)}")
44
+ return v
45
+
46
+
47
+ _ADDRESS_HELP = (
48
+ "Shipping address as a JSON object: street, city, state, postal_code, country "
49
+ "(plus contact_name / phone if known)."
50
+ )
51
+
52
+
53
+ def register(app: typer.Typer) -> None:
54
+ @command(
55
+ app,
56
+ "admin",
57
+ "create",
58
+ "shipping-order",
59
+ api="POST /admin/shipping_orders/create",
60
+ side_effects=[
61
+ "links the orders to the new shipment (the brand sees it on its Ship page)",
62
+ "adds the fulfilment service fee for the brand's plan",
63
+ ],
64
+ approval="ask",
65
+ help=(
66
+ "POST /admin/shipping_orders/create — make a shipment for a brand's production "
67
+ "and/or sampling orders. The shipment belongs to the orders' owner; you are "
68
+ "recorded as created_by. Orders already on a live shipment are refused (unlink "
69
+ "first), and one shipment carries one supplier's goods."
70
+ ),
71
+ )
72
+ def create_shipping_order(
73
+ production_order: list[str] | None = typer.Option(
74
+ None, "--production-order", "-p", help="Production order id; repeat for several."
75
+ ),
76
+ sampling_order: list[str] | None = typer.Option(
77
+ None, "--sampling-order", "-s", help="Sampling order id; repeat for several."
78
+ ),
79
+ mode: str | None = typer.Option(
80
+ None,
81
+ "--mode",
82
+ help=f"Freight method the brand wants quoted: {', '.join(SHIPPING_MODES)}. "
83
+ "Leave out to keep it open.",
84
+ ),
85
+ status: str | None = typer.Option(
86
+ None,
87
+ "--status",
88
+ help="Starting status (default SUBMITTED, i.e. waiting for freight quotes).",
89
+ ),
90
+ address: str | None = typer.Option(
91
+ None,
92
+ "--address",
93
+ help=_ADDRESS_HELP + " Default: the first order's shipping address.",
94
+ ),
95
+ address_file: Path | None = typer.Option(
96
+ None, "--address-file", help="Same as --address, read from a file."
97
+ ),
98
+ reference_id: str | None = typer.Option(
99
+ None, "--reference-id", help="External reference (default admin_<timestamp>)."
100
+ ),
101
+ ) -> None:
102
+ """Create a shipping order for a brand."""
103
+ if not production_order and not sampling_order:
104
+ raise typer.BadParameter("pass at least one --production-order or --sampling-order")
105
+ body: dict[str, Any] = {
106
+ "production_order_ids": production_order or [],
107
+ "sampling_order_ids": sampling_order or [],
108
+ }
109
+ shipping_address = _address(address, address_file)
110
+ if shipping_address is not None:
111
+ body["shipping_address"] = shipping_address
112
+ if mode:
113
+ body["shipping_mode"] = _choice(mode, SHIPPING_MODES, "--mode")
114
+ if status:
115
+ body["status"] = _choice(status, SHIPPING_STATUSES, "--status")
116
+ if reference_id:
117
+ body["reference_id"] = reference_id
118
+ print_json_or_exit(console, lambda: api_client.admin_create_shipping_order(body))
119
+
120
+ @command(
121
+ app,
122
+ "admin",
123
+ "get",
124
+ "shipping-order",
125
+ api="GET /admin/shipping_orders/{shipping_order_id}/details",
126
+ help=(
127
+ "GET /admin/shipping_orders/{id}/details — one shipment with its production and "
128
+ "sampling orders, freight quotes, fees, tracking and address."
129
+ ),
130
+ )
131
+ def get_shipping_order(
132
+ shipping_order_id: str = typer.Argument(..., help="Shipping order id."),
133
+ ) -> None:
134
+ """Show one shipping order."""
135
+ print_json_or_exit(console, lambda: api_client.admin_get_shipping_order(shipping_order_id))
136
+
137
+ @command(
138
+ app,
139
+ "admin",
140
+ "update",
141
+ "shipping-order",
142
+ api="PUT /admin/shipping_orders/{shipping_order_id}/status",
143
+ side_effects=[
144
+ "--status IN_TRANSIT stamps the departure date the brand's ETA is counted from",
145
+ "each group of flags is its own request; a later one failing leaves earlier ones applied",
146
+ ],
147
+ approval="ask",
148
+ help=(
149
+ "Change a shipment: --status (PUT .../status), tracking flags (POST .../tracking), "
150
+ "--address (PUT .../shipping-address), --cartons / --gross-weight-kg "
151
+ "(PUT .../cargo). Pass any combination; they run in that order."
152
+ ),
153
+ )
154
+ def update_shipping_order(
155
+ shipping_order_id: str = typer.Argument(..., help="Shipping order id."),
156
+ status: str | None = typer.Option(
157
+ None, "--status", help=f"New status: {', '.join(SHIPPING_STATUSES)}."
158
+ ),
159
+ tracking_number: str | None = typer.Option(
160
+ None, "--tracking-number", help="Carrier tracking number."
161
+ ),
162
+ tracking_url: str | None = typer.Option(None, "--tracking-url", help="Tracking page URL."),
163
+ shipping_method: str | None = typer.Option(
164
+ None, "--shipping-method", help="Free text, e.g. 'DHL Express'."
165
+ ),
166
+ carrier_id: str | None = typer.Option(
167
+ None, "--carrier-id", help="Carrier code (17track numbering), optional."
168
+ ),
169
+ address: str | None = typer.Option(None, "--address", help=_ADDRESS_HELP),
170
+ address_file: Path | None = typer.Option(
171
+ None, "--address-file", help="Same as --address, read from a file."
172
+ ),
173
+ cartons: int | None = typer.Option(None, "--cartons", help="Number of cartons."),
174
+ gross_weight_kg: float | None = typer.Option(
175
+ None, "--gross-weight-kg", help="Total gross weight in kg."
176
+ ),
177
+ ) -> None:
178
+ """Update status, tracking, address and/or cargo of a shipping order."""
179
+ new_status = _choice(status, SHIPPING_STATUSES, "--status")
180
+ tracking: dict[str, Any] = {
181
+ k: v
182
+ for k, v in {
183
+ "tracking_number": tracking_number,
184
+ "tracking_url": tracking_url,
185
+ "shipping_method": shipping_method,
186
+ "carrier_id": carrier_id,
187
+ }.items()
188
+ if v is not None
189
+ }
190
+ if tracking and not (tracking_number or tracking_url or shipping_method):
191
+ raise typer.BadParameter(
192
+ "--carrier-id needs --tracking-number, --tracking-url or --shipping-method"
193
+ )
194
+ shipping_address = _address(address, address_file)
195
+ cargo: dict[str, Any] = {}
196
+ if cartons is not None:
197
+ cargo["cartons"] = cartons
198
+ if gross_weight_kg is not None:
199
+ cargo["gross_weight_kg"] = gross_weight_kg
200
+ if not (new_status or tracking or shipping_address is not None or cargo):
201
+ raise typer.BadParameter(
202
+ "nothing to change: pass --status, tracking flags, --address or cargo flags"
203
+ )
204
+
205
+ def run() -> Any:
206
+ results: dict[str, Any] = {}
207
+ if new_status:
208
+ results["status"] = api_client.admin_update_shipping_order_status(
209
+ shipping_order_id, new_status
210
+ )
211
+ if tracking:
212
+ results["tracking"] = api_client.admin_update_shipping_order_tracking(
213
+ shipping_order_id, tracking
214
+ )
215
+ if shipping_address is not None:
216
+ results["shipping_address"] = api_client.admin_update_shipping_order_address(
217
+ shipping_order_id, shipping_address
218
+ )
219
+ if cargo:
220
+ results["cargo"] = api_client.admin_update_shipping_order_cargo(
221
+ shipping_order_id, cargo
222
+ )
223
+ return results
224
+
225
+ print_json_or_exit(console, run)
226
+
227
+ @command(
228
+ app,
229
+ "admin",
230
+ "delete",
231
+ "shipping-order",
232
+ api="DELETE /admin/shipping_orders/{shipping_order_id}/delete",
233
+ side_effects=[
234
+ "soft-deletes the shipment",
235
+ "unlinks its production and sampling orders so they can ship again",
236
+ ],
237
+ approval="never",
238
+ help=(
239
+ "DELETE /admin/shipping_orders/{id}/delete — remove a shipment and free its orders. "
240
+ "Graded `never`: the backend does not check status, so it removes a paid or in-transit shipment too."
241
+ ),
242
+ )
243
+ def delete_shipping_order(
244
+ shipping_order_id: str = typer.Argument(..., help="Shipping order id."),
245
+ ) -> None:
246
+ """Delete a shipping order."""
247
+ print_json_or_exit(
248
+ console, lambda: api_client.admin_delete_shipping_order(shipping_order_id)
249
+ )
250
+
251
+ @command(
252
+ app,
253
+ "admin",
254
+ "delete",
255
+ "shipping-order-item",
256
+ api="POST /admin/production_orders/{id}/unlink_from_shipping_order/{shipping_order_id}",
257
+ side_effects=[
258
+ "takes the order off the shipment",
259
+ "deletes the shipment if that was its last order",
260
+ ],
261
+ approval="ask",
262
+ help=(
263
+ "POST /admin/{production|sampling}_orders/{id}/unlink_from_shipping_order/{so_id} — "
264
+ "take one production or sampling order off a shipment. A shipment left empty is "
265
+ "deleted."
266
+ ),
267
+ )
268
+ def delete_shipping_order_item(
269
+ shipping_order_id: str = typer.Argument(..., help="Shipping order id."),
270
+ production_order: str | None = typer.Option(
271
+ None, "--production-order", "-p", help="Production order to take off."
272
+ ),
273
+ sampling_order: str | None = typer.Option(
274
+ None, "--sampling-order", "-s", help="Sampling order to take off."
275
+ ),
276
+ ) -> None:
277
+ """Unlink one order from a shipping order."""
278
+ if bool(production_order) == bool(sampling_order):
279
+ raise typer.BadParameter("pass exactly one of --production-order / --sampling-order")
280
+ print_json_or_exit(
281
+ console,
282
+ lambda: api_client.admin_unlink_from_shipping_order(
283
+ shipping_order_id,
284
+ production_order_id=production_order,
285
+ sampling_order_id=sampling_order,
286
+ ),
287
+ )
288
+
289
+ @command(
290
+ app,
291
+ "admin",
292
+ "create",
293
+ "shipping-option",
294
+ api="POST /admin/shipping_orders/{shipping_order_id}/shipping-options",
295
+ side_effects=[
296
+ "adds a freight quote the brand can pick and pay for",
297
+ "the first quote on a shipment is auto-selected",
298
+ ],
299
+ approval="ask",
300
+ help=(
301
+ "POST /admin/shipping_orders/{id}/shipping-options — add one freight quote "
302
+ "(provider, speed, price, currency)."
303
+ ),
304
+ )
305
+ def create_shipping_option(
306
+ shipping_order_id: str = typer.Argument(..., help="Shipping order id."),
307
+ provider: str = typer.Option(..., "--provider", help="Forwarder / carrier, e.g. DHL."),
308
+ speed: str = typer.Option(..., "--speed", help="e.g. Standard, Express, Sea freight."),
309
+ price: str = typer.Option(..., "--price", help="Price as a number, e.g. 412.50."),
310
+ currency: str = typer.Option(..., "--currency", help="ISO code, e.g. GBP."),
311
+ days: int | None = typer.Option(None, "--days", help="Estimated delivery days."),
312
+ description: str | None = typer.Option(None, "--description", help="Shown to the brand."),
313
+ valid_until: str | None = typer.Option(
314
+ None, "--valid-until", help="Quote valid until, YYYY-MM-DD."
315
+ ),
316
+ includes_duties: bool | None = typer.Option(
317
+ None,
318
+ "--includes-duties/--excludes-duties",
319
+ help="Whether the price includes destination customs and duties.",
320
+ ),
321
+ selected: bool = typer.Option(
322
+ False, "--selected", help="Mark this quote as the chosen one."
323
+ ),
324
+ ) -> None:
325
+ """Add a freight quote to a shipping order."""
326
+ option: dict[str, Any] = {
327
+ "provider": provider,
328
+ "speed": speed,
329
+ "price": price,
330
+ "currency": currency.strip().upper(),
331
+ "selected": selected,
332
+ }
333
+ if days is not None:
334
+ option["estimated_delivery_days"] = days
335
+ if description:
336
+ option["description"] = description
337
+ if valid_until:
338
+ option["valid_until"] = valid_until
339
+ if includes_duties is not None:
340
+ option["includes_customs_and_duties"] = includes_duties
341
+ print_json_or_exit(
342
+ console, lambda: api_client.admin_add_shipping_option(shipping_order_id, option)
343
+ )
344
+
345
+ @command(
346
+ app,
347
+ "admin",
348
+ "delete",
349
+ "shipping-option",
350
+ api="DELETE /admin/shipping_orders/{shipping_order_id}/shipping-options/{shipping_option_id}/remove",
351
+ side_effects=["removes the freight quote from the brand's choices"],
352
+ approval="ask",
353
+ help="DELETE .../shipping-options/{option_id}/remove — withdraw one freight quote.",
354
+ )
355
+ def delete_shipping_option(
356
+ shipping_order_id: str = typer.Argument(..., help="Shipping order id."),
357
+ shipping_option_id: str = typer.Argument(..., help="Shipping option id."),
358
+ ) -> None:
359
+ """Remove a freight quote from a shipping order."""
360
+ print_json_or_exit(
361
+ console,
362
+ lambda: api_client.admin_remove_shipping_option(shipping_order_id, shipping_option_id),
363
+ )
364
+
365
+ @command(
366
+ app,
367
+ "admin",
368
+ "create",
369
+ "shipping-fee",
370
+ api="POST /admin/shipping_orders/{shipping_order_id}/fees/add",
371
+ side_effects=["adds a charge to what the brand pays for this shipment"],
372
+ approval="ask",
373
+ help=(
374
+ "POST /admin/shipping_orders/{id}/fees/add — add a fee (fixed amount or percentage) "
375
+ "on top of the freight."
376
+ ),
377
+ )
378
+ def create_shipping_fee(
379
+ shipping_order_id: str = typer.Argument(..., help="Shipping order id."),
380
+ fee_type: str = typer.Option(..., "--fee-type", help=f"One of {', '.join(FEE_TYPES)}."),
381
+ name: str = typer.Option(..., "--name", help="Label the brand sees."),
382
+ value: str = typer.Option(
383
+ ..., "--value", help="Amount (FIXED) or percent (PERCENTAGE, e.g. 10 for 10%)."
384
+ ),
385
+ calculation_type: str = typer.Option(
386
+ "FIXED", "--calculation-type", help="FIXED or PERCENTAGE."
387
+ ),
388
+ description: str | None = typer.Option(None, "--description", help="Optional note."),
389
+ ) -> None:
390
+ """Add a fee to a shipping order."""
391
+ fee: dict[str, Any] = {
392
+ "fee_type": _choice(fee_type, FEE_TYPES, "--fee-type"),
393
+ "name": name,
394
+ "value": value,
395
+ "calculation_type": _choice(
396
+ calculation_type, ("FIXED", "PERCENTAGE"), "--calculation-type"
397
+ ),
398
+ }
399
+ if description:
400
+ fee["description"] = description
401
+ print_json_or_exit(
402
+ console, lambda: api_client.admin_add_shipping_order_fee(shipping_order_id, fee)
403
+ )
404
+
405
+ @command(
406
+ app,
407
+ "admin",
408
+ "delete",
409
+ "shipping-fee",
410
+ api="DELETE /admin/shipping_orders/{shipping_order_id}/fees/{fee_id}/remove",
411
+ side_effects=["removes the charge from what the brand pays"],
412
+ approval="ask",
413
+ help="DELETE /admin/shipping_orders/{id}/fees/{fee_id}/remove — remove one fee.",
414
+ )
415
+ def delete_shipping_fee(
416
+ shipping_order_id: str = typer.Argument(..., help="Shipping order id."),
417
+ fee_id: str = typer.Argument(..., help="Fee id (from admin get shipping-order)."),
418
+ ) -> None:
419
+ """Remove a fee from a shipping order."""
420
+ print_json_or_exit(
421
+ console, lambda: api_client.admin_remove_shipping_order_fee(shipping_order_id, fee_id)
422
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: makea-cli
3
- Version: 0.2.16
3
+ Version: 0.2.17
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
@@ -93,10 +93,17 @@ exit code:`0` 成功 · `2` 用法错 · `3` 未登录或身份与 persona 不
93
93
  | `makea-cli admin create library-element` | write(auto) | `POST /admin/designer/{user_id}/library_components` | save a reusable component under a designer. Provide --section and --values (see admin get library-schema). | `library-create` |
94
94
  | `makea-cli admin create quote-request` | write(ask) | `POST /admin/supplier/create_quote_request` | create/send a quote request to a supplier for a product (admin). | `create-quote-request` |
95
95
  | `makea-cli admin create rule` | write(ask) | `POST /admin/playbook/users/{user}/rules` | add a rule to one person's AI playbook (user_id or email); admin-authored, active unless --status. Refuses a repeat or a conflict unless --force. --reason required. | — |
96
+ | `makea-cli admin create shipping-fee` | write(ask) | `POST /admin/shipping_orders/{shipping_order_id}/fees/add` | add a fee (fixed amount or percentage) on top of the freight. | — |
97
+ | `makea-cli admin create shipping-option` | write(ask) | `POST /admin/shipping_orders/{shipping_order_id}/shipping-options` | add one freight quote (provider, speed, price, currency). | — |
98
+ | `makea-cli admin create shipping-order` | write(ask) | `POST /admin/shipping_orders/create` | make a shipment for a brand's production and/or sampling orders. The shipment belongs to the orders' owner; you are r… | — |
96
99
  | `makea-cli admin create supplier` | write(ask) | `POST /admin/supplier/add_supplier` | create supplier from JSON file (admin). Run `makea-cli get-supplier-profile-schema` FIRST to get the field taxonomy (… | `add-supplier` |
97
100
  | `makea-cli admin create user` | write(never) | `POST /admin/users/create` | register a user and confirm the Cognito account in one step (no verification email). Allowed --user-type values: "Fas… | `create-user` |
98
101
  | `makea-cli admin delete library-element` | write(never) | `DELETE /admin/designer/{user_id}/library_components/{component_id}` | remove a component from the designer's org library. | `library-delete` |
99
102
  | `makea-cli admin delete rule` | write(ask) | `DELETE /admin/playbook/users/{user}/rules/{rule_id}` | remove a rule: archived, with who and why kept on it (`update rule --status active` restores it). --reason required. | — |
103
+ | `makea-cli admin delete shipping-fee` | write(ask) | `DELETE /admin/shipping_orders/{shipping_order_id}/fees/{fee_id}/remove` | remove one fee. | — |
104
+ | `makea-cli admin delete shipping-option` | write(ask) | `DELETE /admin/shipping_orders/{shipping_order_id}/shipping-options/{shipping_option_id}/remove` | withdraw one freight quote. | — |
105
+ | `makea-cli admin delete shipping-order` | write(never) | `DELETE /admin/shipping_orders/{shipping_order_id}/delete` | remove a shipment and free its orders. Graded `never`: the backend does not check status, so it removes a paid or in-transit shipment too. | — |
106
+ | `makea-cli admin delete shipping-order-item` | write(ask) | `POST /admin/production_orders/{id}/unlink_from_shipping_order/{shipping_order_id}` | take one production or sampling order off a shipment. A shipment left empty is deleted. | — |
100
107
  | `makea-cli admin download document` | read | `GET /admin/document/download` | save a document file (document id + designer user_id). | `download-document` |
101
108
  | `makea-cli admin download product-document` | read | `GET /admin/document/download` | Resolve product via admin list product-order payload, then GET /admin/document/download. | `download-product-document` |
102
109
  | `makea-cli admin generate pricing-groups` | write(ask) | `POST /admin/production_orders/{id}/details/update` | Lay a production order's variants into pricing groups along one axis (--by style \| colour \| variant \| all) with th… | `production-groups-generate` |
@@ -109,6 +116,7 @@ exit code:`0` 成功 · `2` 用法错 · `3` 未登录或身份与 persona 不
109
116
  | `makea-cli admin get quote-request` | read | `GET /admin/quote_requests/{quote_request_id}` | one quote request in full. | — |
110
117
  | `makea-cli admin get revenue-metrics` | read | `GET /admin/business-metrics/cumulative-payment-and-platform-revenue` | platform-wide payment received and platform revenue (service fee + transaction fee) bucketed over a date range. | `get-cumulative-payment-and-platform-revenue` |
111
118
  | `makea-cli admin get rule` | read | `GET /admin/playbook/users/{user}/rules/{rule_id}` | one rule in full, with its evidence: where it came from, who changed it and why. | — |
119
+ | `makea-cli admin get shipping-order` | read | `GET /admin/shipping_orders/{shipping_order_id}/details` | one shipment with its production and sampling orders, freight quotes, fees, tracking and address. | — |
112
120
  | `makea-cli admin get supplier-profile-schema` | read | `GET /admin/supplier/profile/schema` | supplier profile v2 schema (section/field taxonomy: sections, fields, options, required rules). | `get-supplier-profile-schema` |
113
121
  | `makea-cli admin get tech-spec-extraction` | read | `GET /admin/products/{id}/extract-tech-specifications` | AI-generate specs (not saved). | `extract-ai-tech-spec` |
114
122
  | `makea-cli admin get top-clicks` | read | `POST /admin/analytics/query` | which buttons/elements get clicked most. | `analytics-top-clicks` |
@@ -134,6 +142,7 @@ exit code:`0` 成功 · `2` 用法错 · `3` 未登录或身份与 persona 不
134
142
  | `makea-cli admin update quote` | write(ask) | `POST /admin/product/edit_supplier_link_override_info` | admin updates an existing quote (supplier link) in place. Only the options you pass are sent, and the backend leaves … | `update-supplier-quote` |
135
143
  | `makea-cli admin update quote-request` | write(ask) | `PATCH /admin/quote_requests/{quote_request_id}/update` | set a quote request's stage, status and/or due date. Only what you pass is changed. This is the door that unblocks a … | `update-quote-request` |
136
144
  | `makea-cli admin update rule` | write(ask) | `PATCH /admin/playbook/users/{user}/rules/{rule_id}` | change a rule's text, status, applies_to, when or mode. --reason required; the rule keeps what it was before. | — |
145
+ | `makea-cli admin update shipping-order` | write(ask) | `PUT /admin/shipping_orders/{shipping_order_id}/status` | Change a shipment: --status (PUT .../status), tracking flags (POST .../tracking), --address (PUT .../shipping-address… | — |
137
146
  | `makea-cli admin update supplier` | write(ask) | `PUT /admin/supplier/update_supplier` | update an existing supplier from a JSON file (admin). Run `makea-cli get-supplier-profile-schema` FIRST to get the fi… | `update-supplier` |
138
147
  | `makea-cli admin update tech-spec` | write(ask) | `POST /admin/products/{id}/technical-specifications` | persist specs from a JSON file. | `save-product-tech-spec` |
139
148
  | `makea-cli admin update tracking-status` | write(ask) | `POST /admin/tracking/carrier_status` | apply a carrier status looked up by hand (tracking-sweep skill) to one order: status, fan-out, delivered emails (--silent: state only). | — |
@@ -1,6 +1,6 @@
1
- makea_cli/__init__.py,sha256=nS1Qf8CA6pxMzqGCeAMZHTKK2igCSNakZ3o7kDK0JIQ,23
1
+ makea_cli/__init__.py,sha256=Qi17SZ8BBtfJyUgJ1-dmlKGJmEwoMRO4CvqGYPFRAhw,23
2
2
  makea_cli/__main__.py,sha256=AkQjxWfKE3w9iD1d6I-2Zqvv2t9cct0oYyFF0UiA-Z0,144
3
- makea_cli/api_client.py,sha256=I3w5xPMSxZ0bUliXdS8Sz4_W0-ZS3uhHVvoAkn166ZE,67533
3
+ makea_cli/api_client.py,sha256=sPJ_YmfzlpyOi8HL95pDEnQin_eZIN00Yc9LD4fj4XE,70913
4
4
  makea_cli/auth_pkce.py,sha256=iXSyJ7DM3qVuj-3FwPiqM25CxXz9ebFs9FNthZdw3ws,7315
5
5
  makea_cli/broker.py,sha256=5O650nAdRuF-u9E1MuVM2jVv796iBdxJGIaJasxHij0,3019
6
6
  makea_cli/config.py,sha256=hz8MnJSkhxcIEZkcpHR005JrDceo1Xmi8qwIUq50cMU,3003
@@ -10,7 +10,7 @@ makea_cli/main.py,sha256=nuTQ8_P8OSdRbjMHMB4qcKdex124nS-ApCW-zfbqlI0,7169
10
10
  makea_cli/output.py,sha256=VWvZe2Ad8zxT4ZQyBhFRc6wkFwbyYzQFPddepANGAbY,6361
11
11
  makea_cli/posthog_client.py,sha256=k0cy9Hq7bhdLlPvC1ZW_esJ70xHDdQzPQUlx-GpVskk,1921
12
12
  makea_cli/registry.py,sha256=GYJK_idhwIh1X2ictwFjn8PEMaXT56hFVkoLEVYrVqw,13722
13
- makea_cli/commands/__init__.py,sha256=LJ6pjYtIL_VvdSRbF1QomSojcarJF710Sifg2RWsgQo,1575
13
+ makea_cli/commands/__init__.py,sha256=A6nBCidMsRXfNtIPhJhbvIQklYe1BIphMAubZQc1s0w,1676
14
14
  makea_cli/commands/_util.py,sha256=zSFwWaWpncARlKxgq4WuJlJvNv7XdZ39HfHrNOELHuw,980
15
15
  makea_cli/commands/analytics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  makea_cli/commands/analytics/cmd.py,sha256=KVLUtYSeyYZ8_09jIuIAmePjQqcpQS2mb5PBBzGwQYQ,6562
@@ -41,9 +41,11 @@ makea_cli/commands/product/cmd.py,sha256=W0oVTZvsK1QWZWv2x6NC26_JQMXrO16O8gYuKYP
41
41
  makea_cli/commands/production/__init__.py,sha256=Whn5ziwVMt4jzcokiFWLhWIpRLYC-ldnjZgAzeQ3We4,79
42
42
  makea_cli/commands/production/cmd.py,sha256=s6fVAGzOvokF6Va5WKGEA2vw2x2hDQ3PrsF01EyZA-Q,38283
43
43
  makea_cli/commands/production/pricing_spec.py,sha256=xGbj7-ps3PcKuU6Oc59A70b62FjF6Awhr5IY3ymZveU,27174
44
+ makea_cli/commands/shipping/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
+ makea_cli/commands/shipping/cmd.py,sha256=ozt73pj3onlOv0NhEwiJlslyG9WOycE0QN4tmzwFlT0,17280
44
46
  makea_cli/commands/supplier/__init__.py,sha256=qfQDeQdQ-B7I_uwFVfcp1vXFUTL4fAcNLu3ipsZ7dfs,77
45
47
  makea_cli/commands/supplier/cmd.py,sha256=crRCy9WfhkryHMIL3iufoiT90_SG3-mKChAtdhd06uc,58076
46
- makea_cli-0.2.16.dist-info/METADATA,sha256=-Zpj9hQq0JaECBulxaELeFJeaMEjjthhrIDO8VxBeKI,32987
47
- makea_cli-0.2.16.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
48
- makea_cli-0.2.16.dist-info/entry_points.txt,sha256=4x-lbVa1jBbNlkNq7L-rYyfRh2spZmc3BLIzP-Dl-Qk,50
49
- makea_cli-0.2.16.dist-info/RECORD,,
48
+ makea_cli-0.2.17.dist-info/METADATA,sha256=aubC1OdePkFzgJBiKYJPPEk1-wtjC7jwfckx2JA4OgU,34938
49
+ makea_cli-0.2.17.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
50
+ makea_cli-0.2.17.dist-info/entry_points.txt,sha256=4x-lbVa1jBbNlkNq7L-rYyfRh2spZmc3BLIzP-Dl-Qk,50
51
+ makea_cli-0.2.17.dist-info/RECORD,,