wildberries-cli 0.1.0__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.
@@ -0,0 +1 @@
1
+ """wildberries-cli package."""
@@ -0,0 +1,65 @@
1
+ """CLI argument parsing helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from datetime import datetime
7
+ from pathlib import Path
8
+ from typing import Any, Iterable
9
+
10
+ from wildberries_cli.output import read_text_arg
11
+
12
+
13
+ def load_json_input(body_json: str | None = None, body_file: str | None = None) -> Any:
14
+ """Load JSON from one source (`--body-json` or `--body-file`)."""
15
+ if body_json and body_file:
16
+ raise ValueError("Use only one of --body-json or --body-file")
17
+ if body_json is None and body_file is None:
18
+ raise ValueError("Body JSON is required")
19
+
20
+ if body_json is not None:
21
+ text = read_text_arg(body_json)
22
+ else:
23
+ if body_file == "-":
24
+ text = read_text_arg("-")
25
+ else:
26
+ text = Path(str(body_file)).read_text()
27
+
28
+ return json.loads(text)
29
+
30
+
31
+ def parse_rfc3339ish(value: str) -> datetime:
32
+ """Parse an RFC3339-ish datetime/date string accepted by the SDK."""
33
+ text = value.strip()
34
+ if text.endswith("Z"):
35
+ text = text[:-1] + "+00:00"
36
+ try:
37
+ return datetime.fromisoformat(text)
38
+ except ValueError as exc:
39
+ raise ValueError(f"Invalid datetime '{value}'. Use ISO/RFC3339 (e.g. 2024-01-01 or 2024-01-01T12:00:00+03:00).") from exc
40
+
41
+
42
+ def parse_kv_pairs(items: Iterable[str]) -> dict[str, str]:
43
+ out: dict[str, str] = {}
44
+ for item in items:
45
+ key, value = _split_kv(item)
46
+ out[key] = value
47
+ return out
48
+
49
+
50
+ def parse_json_kv_pairs(items: Iterable[str]) -> dict[str, Any]:
51
+ out: dict[str, Any] = {}
52
+ for item in items:
53
+ key, value = _split_kv(item)
54
+ out[key] = json.loads(value)
55
+ return out
56
+
57
+
58
+ def _split_kv(item: str) -> tuple[str, str]:
59
+ if "=" not in item:
60
+ raise ValueError(f"Expected KEY=VALUE, got '{item}'")
61
+ key, value = item.split("=", 1)
62
+ key = key.strip()
63
+ if not key:
64
+ raise ValueError(f"Expected KEY=VALUE, got '{item}'")
65
+ return key, value
@@ -0,0 +1,159 @@
1
+ """Dynamic Wildberries SDK client factory and API invocation helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import importlib
6
+ import inspect
7
+ import pkgutil
8
+ import time
9
+ from dataclasses import dataclass
10
+ from typing import TYPE_CHECKING, Any
11
+
12
+ import typer
13
+ import wildberries_sdk
14
+
15
+ from wildberries_cli.output import print_error
16
+
17
+ if TYPE_CHECKING:
18
+ from wildberries_cli.config import Config
19
+
20
+
21
+ @dataclass
22
+ class ModuleClient:
23
+ module_name: str
24
+ sdk_module: Any
25
+ api: Any
26
+
27
+
28
+ def list_sdk_modules() -> list[str]:
29
+ return sorted(m.name for m in pkgutil.iter_modules(wildberries_sdk.__path__))
30
+
31
+
32
+ def list_methods(module_name: str) -> list[str]:
33
+ cls = _get_default_api_class(normalize_module_name(module_name))
34
+ methods = []
35
+ for name, fn in inspect.getmembers(cls, inspect.isfunction):
36
+ if name.startswith("_"):
37
+ continue
38
+ methods.append(name)
39
+ return sorted(methods)
40
+
41
+
42
+ def method_signature(module_name: str, method_name: str) -> str:
43
+ cls = _get_default_api_class(normalize_module_name(module_name))
44
+ fn = getattr(cls, method_name, None)
45
+ if fn is None or not callable(fn):
46
+ raise AttributeError(f"Method '{method_name}' not found in module '{module_name}'")
47
+ return f"{method_name}{inspect.signature(fn)}"
48
+
49
+
50
+ def get_module_client(module_name: str, cfg: "Config", *, require_token: bool = True) -> ModuleClient:
51
+ module_name = normalize_module_name(module_name)
52
+ if require_token:
53
+ _require_token(cfg)
54
+
55
+ sdk_module = importlib.import_module(f"wildberries_sdk.{module_name}")
56
+ Configuration = getattr(sdk_module, "Configuration")
57
+ ApiClient = getattr(sdk_module, "ApiClient")
58
+ DefaultApi = getattr(sdk_module, "DefaultApi")
59
+
60
+ conf_kwargs: dict[str, Any] = {}
61
+ if cfg.api_token:
62
+ conf_kwargs["api_key"] = {"HeaderApiKey": cfg.api_token}
63
+ if cfg.retries:
64
+ conf_kwargs["retries"] = cfg.retries
65
+ conf = Configuration(**conf_kwargs)
66
+ api_client = ApiClient(conf)
67
+ api = DefaultApi(api_client)
68
+ return ModuleClient(module_name=module_name, sdk_module=sdk_module, api=api)
69
+
70
+
71
+ def call_api(
72
+ module_name: str,
73
+ method_name: str,
74
+ cfg: "Config",
75
+ *,
76
+ require_token: bool = True,
77
+ **kwargs: Any,
78
+ ) -> Any:
79
+ client = get_module_client(module_name, cfg, require_token=require_token)
80
+ fn = getattr(client.api, method_name, None)
81
+ if fn is None or not callable(fn):
82
+ print_error("validation_error", f"Unknown method '{method_name}' for module '{client.module_name}'")
83
+ raise typer.Exit(1)
84
+ return call_with_retry(fn, cfg, **kwargs)
85
+
86
+
87
+ def call_with_retry(fn: Any, cfg: "Config", **kwargs: Any) -> Any:
88
+ sig = inspect.signature(fn)
89
+ if "_request_timeout" in sig.parameters and "_request_timeout" not in kwargs:
90
+ kwargs["_request_timeout"] = cfg.timeout_seconds
91
+
92
+ max_attempts = max(1, int(cfg.retries))
93
+ for attempt in range(max_attempts):
94
+ try:
95
+ return fn(**kwargs)
96
+ except Exception as exc:
97
+ status = getattr(exc, "status", None)
98
+ if status in {429, 500, 502, 503, 504} and attempt < max_attempts - 1:
99
+ delay = _retry_after_seconds(exc) or min(2 ** attempt, 10)
100
+ time.sleep(delay)
101
+ continue
102
+ _handle_exception(exc)
103
+ raise typer.Exit(1)
104
+
105
+ raise typer.Exit(1)
106
+
107
+
108
+ def normalize_module_name(value: str) -> str:
109
+ return value.replace("-", "_").strip()
110
+
111
+
112
+ def _require_token(cfg: "Config") -> None:
113
+ if not cfg.api_token:
114
+ print_error(
115
+ "auth_error",
116
+ "No WB API token configured. Set WB_API_TOKEN, use --api-token, or run `wb config init`.",
117
+ )
118
+ raise typer.Exit(1)
119
+
120
+
121
+ def _get_default_api_class(module_name: str) -> Any:
122
+ sdk_module = importlib.import_module(f"wildberries_sdk.{module_name}")
123
+ return getattr(sdk_module, "DefaultApi")
124
+
125
+
126
+ def _retry_after_seconds(exc: Exception) -> int | None:
127
+ headers = getattr(exc, "headers", None)
128
+ if not headers:
129
+ return None
130
+ try:
131
+ value = headers.get("Retry-After") # type: ignore[union-attr]
132
+ if value is None:
133
+ return None
134
+ return max(1, int(value))
135
+ except Exception:
136
+ return None
137
+
138
+
139
+ def _handle_exception(exc: Exception) -> None:
140
+ status = getattr(exc, "status", None)
141
+ message = str(exc)
142
+ detail = getattr(exc, "body", None)
143
+
144
+ if status == 401:
145
+ print_error("auth_error", "Authentication failed. Check WB_API_TOKEN.", status_code=status, detail=detail)
146
+ return
147
+ if status == 403:
148
+ print_error("forbidden", "Permission denied or token lacks required scope.", status_code=status, detail=detail)
149
+ return
150
+ if status == 404:
151
+ print_error("not_found", "Resource not found.", status_code=status, detail=detail)
152
+ return
153
+ if status == 429:
154
+ print_error("rate_limit", "Rate limit exceeded.", status_code=status, detail=detail)
155
+ return
156
+ if status is not None:
157
+ print_error("api_error", message, status_code=status, detail=detail)
158
+ return
159
+ print_error("cli_error", message)
@@ -0,0 +1 @@
1
+ """wb command modules."""
@@ -0,0 +1,128 @@
1
+ """communications subcommand (feedbacks/questions)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import typer
6
+
7
+ from wildberries_cli.client import call_api
8
+ from wildberries_cli.config import Config
9
+ from wildberries_cli.output import emit, feedbacks_table, print_error, questions_table, read_text_arg
10
+ from wildberries_cli.serialize import to_data
11
+
12
+ app = typer.Typer(name="communications", help="Buyer communications APIs.", no_args_is_help=True)
13
+ feedbacks_app = typer.Typer(name="feedbacks", help="Feedbacks API.", no_args_is_help=True)
14
+ questions_app = typer.Typer(name="questions", help="Questions API.", no_args_is_help=True)
15
+ app.add_typer(feedbacks_app, name="feedbacks")
16
+ app.add_typer(questions_app, name="questions")
17
+
18
+
19
+ @feedbacks_app.command("list")
20
+ def feedbacks_list(
21
+ ctx: typer.Context,
22
+ answered: bool = typer.Option(..., "--answered/--unanswered", help="Filter by answered state"),
23
+ take: int = typer.Option(100, "--take", min=1, max=5000, help="Number of feedbacks to return"),
24
+ skip: int = typer.Option(0, "--skip", min=0, help="Offset"),
25
+ nm_id: int | None = typer.Option(None, "--nm-id", help="WB nmID"),
26
+ order: str | None = typer.Option(None, "--order", help="dateAsc or dateDesc"),
27
+ date_from: int | None = typer.Option(None, "--date-from", help="Unix timestamp start"),
28
+ date_to: int | None = typer.Option(None, "--date-to", help="Unix timestamp end"),
29
+ ) -> None:
30
+ cfg: Config = ctx.obj
31
+ kwargs = {
32
+ "is_answered": answered,
33
+ "take": take,
34
+ "skip": skip,
35
+ "nm_id": nm_id,
36
+ "order": order,
37
+ "date_from": date_from,
38
+ "date_to": date_to,
39
+ }
40
+ data = to_data(call_api("communications", "api_v1_feedbacks_get", cfg, **{k: v for k, v in kwargs.items() if v is not None}))
41
+ emit(data, pretty=cfg.pretty, table_builder=feedbacks_table)
42
+
43
+
44
+ @feedbacks_app.command("get")
45
+ def feedbacks_get(ctx: typer.Context, feedback_id: str = typer.Argument(..., help="Feedback ID")) -> None:
46
+ cfg: Config = ctx.obj
47
+ data = to_data(call_api("communications", "api_v1_feedback_get", cfg, id=feedback_id))
48
+ emit(data, pretty=cfg.pretty)
49
+
50
+
51
+ @feedbacks_app.command("answer")
52
+ def feedbacks_answer(
53
+ ctx: typer.Context,
54
+ feedback_id: str = typer.Argument(..., help="Feedback ID"),
55
+ text: str = typer.Option(..., "--text", help="Answer text or '-' for stdin"),
56
+ ) -> None:
57
+ cfg: Config = ctx.obj
58
+ try:
59
+ from wildberries_sdk.communications import ApiV1FeedbacksAnswerPostRequest
60
+
61
+ req = ApiV1FeedbacksAnswerPostRequest(id=feedback_id, text=read_text_arg(text))
62
+ except Exception as exc:
63
+ print_error("validation_error", f"Invalid feedback answer payload: {exc}")
64
+ raise typer.Exit(1)
65
+
66
+ data = to_data(call_api("communications", "api_v1_feedbacks_answer_post", cfg, api_v1_feedbacks_answer_post_request=req))
67
+ emit(data if data is not None else {"ok": True, "id": feedback_id}, pretty=cfg.pretty)
68
+
69
+
70
+ @questions_app.command("list")
71
+ def questions_list(
72
+ ctx: typer.Context,
73
+ answered: bool = typer.Option(..., "--answered/--unanswered", help="Filter by answered state"),
74
+ take: int = typer.Option(100, "--take", min=1, max=10000, help="Number of questions to return"),
75
+ skip: int = typer.Option(0, "--skip", min=0, help="Offset"),
76
+ nm_id: int | None = typer.Option(None, "--nm-id", help="WB nmID"),
77
+ order: str | None = typer.Option(None, "--order", help="dateAsc or dateDesc"),
78
+ date_from: int | None = typer.Option(None, "--date-from", help="Unix timestamp start"),
79
+ date_to: int | None = typer.Option(None, "--date-to", help="Unix timestamp end"),
80
+ ) -> None:
81
+ cfg: Config = ctx.obj
82
+ kwargs = {
83
+ "is_answered": answered,
84
+ "take": take,
85
+ "skip": skip,
86
+ "nm_id": nm_id,
87
+ "order": order,
88
+ "date_from": date_from,
89
+ "date_to": date_to,
90
+ }
91
+ data = to_data(call_api("communications", "api_v1_questions_get", cfg, **{k: v for k, v in kwargs.items() if v is not None}))
92
+ emit(data, pretty=cfg.pretty, table_builder=questions_table)
93
+
94
+
95
+ @questions_app.command("get")
96
+ def questions_get(ctx: typer.Context, question_id: str = typer.Argument(..., help="Question ID")) -> None:
97
+ cfg: Config = ctx.obj
98
+ data = to_data(call_api("communications", "api_v1_question_get", cfg, id=question_id))
99
+ emit(data, pretty=cfg.pretty)
100
+
101
+
102
+ @questions_app.command("answer")
103
+ def questions_answer(
104
+ ctx: typer.Context,
105
+ question_id: str = typer.Argument(..., help="Question ID"),
106
+ text: str = typer.Option(..., "--text", help="Answer text or '-' for stdin"),
107
+ state: str = typer.Option("wbRu", "--state", help="Question state (`wbRu` to publish answer, `none` to reject)")
108
+ ) -> None:
109
+ cfg: Config = ctx.obj
110
+ try:
111
+ from wildberries_sdk.communications import (
112
+ ApiV1QuestionsPatchRequest,
113
+ ApiV1QuestionsPatchRequestOneOf1,
114
+ ApiV1QuestionsPatchRequestOneOf1Answer,
115
+ )
116
+
117
+ payload = ApiV1QuestionsPatchRequestOneOf1(
118
+ id=question_id,
119
+ answer=ApiV1QuestionsPatchRequestOneOf1Answer(text=read_text_arg(text)),
120
+ state=state,
121
+ )
122
+ req = ApiV1QuestionsPatchRequest(actual_instance=payload)
123
+ except Exception as exc:
124
+ print_error("validation_error", f"Invalid question answer payload: {exc}")
125
+ raise typer.Exit(1)
126
+
127
+ data = to_data(call_api("communications", "api_v1_questions_patch", cfg, api_v1_questions_patch_request=req))
128
+ emit(data, pretty=cfg.pretty)
@@ -0,0 +1,91 @@
1
+ """config subcommand: show / set / init."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+
7
+ import typer
8
+ from rich.console import Console
9
+ from rich.prompt import Prompt
10
+
11
+ from wildberries_cli.client import call_api
12
+ from wildberries_cli.config import Config, CONFIG_PATH, config_as_dict, load_config, save_config, save_config_key
13
+ from wildberries_cli.output import print_error, print_json
14
+ from wildberries_cli.serialize import to_data
15
+
16
+ app = typer.Typer(name="config", help="Manage wb CLI configuration.", no_args_is_help=True)
17
+ console = Console()
18
+
19
+
20
+ @app.command("show")
21
+ def config_show(
22
+ ctx: typer.Context,
23
+ reveal: bool = typer.Option(False, "--reveal", help="Show full API token."),
24
+ ) -> None:
25
+ cfg: Config = ctx.obj
26
+ print_json(config_as_dict(cfg, reveal=reveal))
27
+
28
+
29
+ @app.command("set")
30
+ def config_set(
31
+ key: str = typer.Argument(..., help="Dotted config key, e.g. core.retries"),
32
+ value: str = typer.Argument(..., help="Value to set"),
33
+ ) -> None:
34
+ try:
35
+ save_config_key(key, value)
36
+ print_json({"ok": True, "key": key, "value": value})
37
+ except Exception as exc:
38
+ print_error("config_error", f"Failed to write config: {exc}")
39
+ raise typer.Exit(1)
40
+
41
+
42
+ @app.command("init")
43
+ def config_init(ctx: typer.Context) -> None:
44
+ if not sys.stdin.isatty():
45
+ print_error("validation_error", "`wb config init` requires an interactive terminal.")
46
+ raise typer.Exit(1)
47
+
48
+ cfg: Config = ctx.obj
49
+ console.print("[bold]wb setup wizard[/bold]")
50
+ console.print(f"Config will be saved to: [dim]{CONFIG_PATH}[/dim]\n")
51
+
52
+ api_token = Prompt.ask("WB API token", password=True, default=cfg.api_token or "")
53
+ timeout_text = Prompt.ask("Request timeout (seconds)", default=str(cfg.timeout_seconds))
54
+ retries_text = Prompt.ask("Retry attempts", default=str(cfg.retries))
55
+ locale_text = Prompt.ask("Default locale (ru/en/zh, optional)", default=cfg.locale or "")
56
+
57
+ if not api_token:
58
+ print_error("validation_error", "WB API token is required.")
59
+ raise typer.Exit(1)
60
+
61
+ try:
62
+ timeout_seconds = float(timeout_text)
63
+ retries = int(retries_text)
64
+ except ValueError:
65
+ print_error("validation_error", "Timeout must be a number and retries must be an integer.")
66
+ raise typer.Exit(1)
67
+
68
+ new_cfg = load_config()
69
+ new_cfg.api_token = api_token
70
+ new_cfg.timeout_seconds = timeout_seconds
71
+ new_cfg.retries = retries
72
+ new_cfg.locale = locale_text or None
73
+
74
+ console.print("\nValidating token with `general.api_v1_seller_info_get`…")
75
+ try:
76
+ result = call_api("general", "api_v1_seller_info_get", new_cfg)
77
+ seller = to_data(result)
78
+ seller_name = seller.get("name") if isinstance(seller, dict) else None
79
+ if seller_name:
80
+ console.print(f"[green]✓[/green] Connected as: {seller_name}")
81
+ else:
82
+ console.print("[green]✓[/green] Token is valid")
83
+ except typer.Exit:
84
+ raise
85
+ except Exception as exc:
86
+ print_error("auth_error", f"Validation failed: {exc}")
87
+ raise typer.Exit(1)
88
+
89
+ save_config(new_cfg)
90
+ console.print(f"\n[green]✓[/green] Config saved to {CONFIG_PATH}")
91
+ print_json(config_as_dict(new_cfg))
@@ -0,0 +1,45 @@
1
+ """general subcommand: ping / seller-info / users."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import typer
6
+
7
+ from wildberries_cli.client import call_api
8
+ from wildberries_cli.config import Config
9
+ from wildberries_cli.output import emit
10
+ from wildberries_cli.serialize import to_data
11
+
12
+ app = typer.Typer(name="general", help="General Wildberries seller APIs.", no_args_is_help=True)
13
+
14
+
15
+ @app.command("ping")
16
+ def ping(ctx: typer.Context) -> None:
17
+ cfg: Config = ctx.obj
18
+ data = to_data(call_api("general", "ping_get", cfg, require_token=False))
19
+ emit(data, pretty=cfg.pretty)
20
+
21
+
22
+ @app.command("seller-info")
23
+ def seller_info(ctx: typer.Context) -> None:
24
+ cfg: Config = ctx.obj
25
+ data = to_data(call_api("general", "api_v1_seller_info_get", cfg))
26
+ emit(data, pretty=cfg.pretty)
27
+
28
+
29
+ @app.command("users")
30
+ def users_list(
31
+ ctx: typer.Context,
32
+ limit: int | None = typer.Option(None, "--limit", help="Max users to return (<=100)"),
33
+ offset: int | None = typer.Option(None, "--offset", help="Pagination offset"),
34
+ invited_only: bool | None = typer.Option(
35
+ None,
36
+ "--invited-only/--active",
37
+ help="List invited (not activated) users or active users",
38
+ ),
39
+ ) -> None:
40
+ cfg: Config = ctx.obj
41
+ kwargs = {"limit": limit, "offset": offset}
42
+ if invited_only is not None:
43
+ kwargs["is_invite_only"] = invited_only
44
+ data = to_data(call_api("general", "api_v1_users_get", cfg, **{k: v for k, v in kwargs.items() if v is not None}))
45
+ emit(data, pretty=cfg.pretty)
@@ -0,0 +1,117 @@
1
+ """orders-fbs subcommand (selected operational endpoints)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import typer
6
+
7
+ from wildberries_cli.client import call_api
8
+ from wildberries_cli.config import Config
9
+ from wildberries_cli.output import emit, fbs_orders_table, print_error
10
+ from wildberries_cli.serialize import to_data
11
+
12
+ app = typer.Typer(name="orders-fbs", help="Orders FBS APIs (selected endpoints).", no_args_is_help=True)
13
+ orders_app = typer.Typer(name="orders", help="FBS orders.", no_args_is_help=True)
14
+ supplies_app = typer.Typer(name="supplies", help="FBS supplies.", no_args_is_help=True)
15
+ app.add_typer(orders_app, name="orders")
16
+ app.add_typer(supplies_app, name="supplies")
17
+
18
+
19
+ @orders_app.command("new")
20
+ def orders_new(ctx: typer.Context) -> None:
21
+ cfg: Config = ctx.obj
22
+ data = to_data(call_api("orders_fbs", "api_v3_orders_new_get", cfg))
23
+ emit(data, pretty=cfg.pretty, table_builder=fbs_orders_table)
24
+
25
+
26
+ @orders_app.command("list")
27
+ def orders_list(
28
+ ctx: typer.Context,
29
+ limit: int = typer.Option(100, "--limit", min=1, max=1000),
30
+ next: int = typer.Option(0, "--next", help="Pagination cursor"),
31
+ date_from: int | None = typer.Option(None, "--date-from", help="Unix timestamp start"),
32
+ date_to: int | None = typer.Option(None, "--date-to", help="Unix timestamp end"),
33
+ ) -> None:
34
+ cfg: Config = ctx.obj
35
+ kwargs = {"limit": limit, "next": next, "date_from": date_from, "date_to": date_to}
36
+ data = to_data(call_api("orders_fbs", "api_v3_orders_get", cfg, **{k: v for k, v in kwargs.items() if v is not None}))
37
+ emit(data, pretty=cfg.pretty, table_builder=fbs_orders_table)
38
+
39
+
40
+ @orders_app.command("status")
41
+ def orders_status(
42
+ ctx: typer.Context,
43
+ order_ids: list[int] = typer.Option(..., "--order", help="Order ID(s). Repeat option up to API limit."),
44
+ ) -> None:
45
+ cfg: Config = ctx.obj
46
+ try:
47
+ from wildberries_sdk.orders_fbs.models.api_v3_orders_status_post_request import ApiV3OrdersStatusPostRequest
48
+
49
+ req = ApiV3OrdersStatusPostRequest(orders=order_ids)
50
+ except Exception as exc:
51
+ print_error("validation_error", f"Invalid status request: {exc}")
52
+ raise typer.Exit(1)
53
+
54
+ data = to_data(call_api("orders_fbs", "api_v3_orders_status_post", cfg, api_v3_orders_status_post_request=req))
55
+ emit(data, pretty=cfg.pretty)
56
+
57
+
58
+ @orders_app.command("stickers")
59
+ def orders_stickers(
60
+ ctx: typer.Context,
61
+ order_ids: list[int] = typer.Option(..., "--order", help="Order ID(s). Repeat option."),
62
+ type: str = typer.Option(..., "--type", help="Sticker type"),
63
+ width: int = typer.Option(..., "--width", help="Sticker width"),
64
+ height: int = typer.Option(..., "--height", help="Sticker height"),
65
+ ) -> None:
66
+ cfg: Config = ctx.obj
67
+ try:
68
+ from wildberries_sdk.orders_fbs.models.api_v3_orders_stickers_post_request import (
69
+ ApiV3OrdersStickersPostRequest,
70
+ )
71
+
72
+ req = ApiV3OrdersStickersPostRequest(orders=order_ids)
73
+ except Exception as exc:
74
+ print_error("validation_error", f"Invalid stickers request: {exc}")
75
+ raise typer.Exit(1)
76
+
77
+ data = to_data(
78
+ call_api(
79
+ "orders_fbs",
80
+ "api_v3_orders_stickers_post",
81
+ cfg,
82
+ type=type,
83
+ width=width,
84
+ height=height,
85
+ api_v3_orders_stickers_post_request=req,
86
+ )
87
+ )
88
+ emit(data, pretty=cfg.pretty)
89
+
90
+
91
+ @supplies_app.command("list")
92
+ def supplies_list(
93
+ ctx: typer.Context,
94
+ limit: int = typer.Option(100, "--limit", min=1, max=1000),
95
+ next: int = typer.Option(0, "--next", help="Pagination cursor"),
96
+ ) -> None:
97
+ cfg: Config = ctx.obj
98
+ data = to_data(call_api("orders_fbs", "api_v3_supplies_get", cfg, limit=limit, next=next))
99
+ emit(data, pretty=cfg.pretty)
100
+
101
+
102
+ @supplies_app.command("create")
103
+ def supplies_create(
104
+ ctx: typer.Context,
105
+ name: str = typer.Option(..., "--name", help="Supply name"),
106
+ ) -> None:
107
+ cfg: Config = ctx.obj
108
+ try:
109
+ from wildberries_sdk.orders_fbs.models.api_v3_supplies_post_request import ApiV3SuppliesPostRequest
110
+
111
+ req = ApiV3SuppliesPostRequest(name=name)
112
+ except Exception as exc:
113
+ print_error("validation_error", f"Invalid supply request: {exc}")
114
+ raise typer.Exit(1)
115
+
116
+ data = to_data(call_api("orders_fbs", "api_v3_supplies_post", cfg, api_v3_supplies_post_request=req))
117
+ emit(data, pretty=cfg.pretty)