amazon-sp-cli 0.1.2__tar.gz → 0.1.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (22) hide show
  1. {amazon_sp_cli-0.1.2/amazon_sp_cli.egg-info → amazon_sp_cli-0.1.4}/PKG-INFO +1 -1
  2. {amazon_sp_cli-0.1.2 → amazon_sp_cli-0.1.4}/amazon_sp_cli/main.py +169 -84
  3. {amazon_sp_cli-0.1.2 → amazon_sp_cli-0.1.4/amazon_sp_cli.egg-info}/PKG-INFO +1 -1
  4. {amazon_sp_cli-0.1.2 → amazon_sp_cli-0.1.4}/amazon_sp_cli.egg-info/SOURCES.txt +1 -1
  5. {amazon_sp_cli-0.1.2 → amazon_sp_cli-0.1.4}/setup.py +1 -1
  6. amazon_sp_cli-0.1.2/tests/test_coupon.py → amazon_sp_cli-0.1.4/tests/test_sale_price.py +23 -28
  7. {amazon_sp_cli-0.1.2 → amazon_sp_cli-0.1.4}/LICENSE +0 -0
  8. {amazon_sp_cli-0.1.2 → amazon_sp_cli-0.1.4}/MANIFEST.in +0 -0
  9. {amazon_sp_cli-0.1.2 → amazon_sp_cli-0.1.4}/README.md +0 -0
  10. {amazon_sp_cli-0.1.2 → amazon_sp_cli-0.1.4}/amazon_sp_cli/__init__.py +0 -0
  11. {amazon_sp_cli-0.1.2 → amazon_sp_cli-0.1.4}/amazon_sp_cli/__main__.py +0 -0
  12. {amazon_sp_cli-0.1.2 → amazon_sp_cli-0.1.4}/amazon_sp_cli/auth.py +0 -0
  13. {amazon_sp_cli-0.1.2 → amazon_sp_cli-0.1.4}/amazon_sp_cli/client.py +0 -0
  14. {amazon_sp_cli-0.1.2 → amazon_sp_cli-0.1.4}/amazon_sp_cli.egg-info/dependency_links.txt +0 -0
  15. {amazon_sp_cli-0.1.2 → amazon_sp_cli-0.1.4}/amazon_sp_cli.egg-info/entry_points.txt +0 -0
  16. {amazon_sp_cli-0.1.2 → amazon_sp_cli-0.1.4}/amazon_sp_cli.egg-info/requires.txt +0 -0
  17. {amazon_sp_cli-0.1.2 → amazon_sp_cli-0.1.4}/amazon_sp_cli.egg-info/top_level.txt +0 -0
  18. {amazon_sp_cli-0.1.2 → amazon_sp_cli-0.1.4}/pyproject.toml +0 -0
  19. {amazon_sp_cli-0.1.2 → amazon_sp_cli-0.1.4}/setup.cfg +0 -0
  20. {amazon_sp_cli-0.1.2 → amazon_sp_cli-0.1.4}/tests/__init__.py +0 -0
  21. {amazon_sp_cli-0.1.2 → amazon_sp_cli-0.1.4}/tests/test_auth.py +0 -0
  22. {amazon_sp_cli-0.1.2 → amazon_sp_cli-0.1.4}/tests/test_client.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: amazon-sp-cli
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: CLI tool for Amazon Selling Partner API (SP-API) operations
5
5
  Home-page: https://github.com/stellaraether/amazon-sp-cli
6
6
  Author: Lunan Li
@@ -1,13 +1,17 @@
1
1
  """Main CLI entry point for Amazon SP-API CLI."""
2
2
 
3
3
  import json
4
+ import os
4
5
  from datetime import datetime, timezone
5
6
 
6
7
  import click
8
+ import yaml
7
9
 
8
10
  from .auth import SPAPIAuth
9
11
  from .client import SPAPIClient
10
12
 
13
+ DEFAULT_CREDENTIALS_PATH = os.path.expanduser("~/.config/amazon-sp-cli/credentials.yml")
14
+
11
15
 
12
16
  def _check_path():
13
17
  """Check if the CLI is accessible in PATH and warn once per day."""
@@ -62,6 +66,122 @@ def cli(ctx, credentials):
62
66
  ctx.obj["client"] = SPAPIClient(ctx.obj["auth"])
63
67
 
64
68
 
69
+ @cli.group()
70
+ def auth():
71
+ """Authentication commands."""
72
+ pass
73
+
74
+
75
+ @auth.command("setup")
76
+ @click.option("--path", default=DEFAULT_CREDENTIALS_PATH, help="Path to save credentials")
77
+ @click.option("--profile", default="default", help="Credential profile name")
78
+ @click.option("--refresh-token", help="Refresh token")
79
+ @click.option("--client-id", help="Client ID")
80
+ @click.option("--client-secret", help="Client secret")
81
+ @click.option("--aws-access-key-id", help="AWS Access Key ID")
82
+ @click.option("--aws-secret-access-key", help="AWS Secret Access Key")
83
+ @click.option("--seller-id", default="A2GKV2AN9F8YG3", help="Seller ID")
84
+ @click.option("--marketplace-id", default="ATVPDKIKX0DER", help="Marketplace ID")
85
+ @click.pass_context
86
+ def auth_setup(
87
+ ctx,
88
+ path,
89
+ profile,
90
+ refresh_token,
91
+ client_id,
92
+ client_secret,
93
+ aws_access_key_id,
94
+ aws_secret_access_key,
95
+ seller_id,
96
+ marketplace_id,
97
+ ):
98
+ """Set up Amazon SP-API credentials.
99
+
100
+ When flags are omitted, falls back to interactive prompts.
101
+ """
102
+ click.echo("🔐 Amazon SP-API Credential Setup")
103
+ click.echo("=" * 50)
104
+ click.echo()
105
+
106
+ interactive = not all([refresh_token, client_id, client_secret, aws_access_key_id, aws_secret_access_key])
107
+ if interactive:
108
+ click.echo("You'll need the following from your Amazon Developer account:")
109
+ click.echo(" 1. Refresh Token (from LWA authorization)")
110
+ click.echo(" 2. Client ID (from your app registration)")
111
+ click.echo(" 3. Client Secret (from your app registration)")
112
+ click.echo(" 4. AWS Access Key ID")
113
+ click.echo(" 5. AWS Secret Access Key")
114
+ click.echo()
115
+
116
+ profile = profile or click.prompt("Profile name", default="default")
117
+ refresh_token = refresh_token or click.prompt("Refresh token", hide_input=True)
118
+ client_id = client_id or click.prompt("Client ID")
119
+ client_secret = client_secret or click.prompt("Client secret", hide_input=True)
120
+ aws_access_key_id = aws_access_key_id or click.prompt("AWS Access Key ID")
121
+ aws_secret_access_key = aws_secret_access_key or click.prompt("AWS Secret Access Key", hide_input=True)
122
+
123
+ credentials = {
124
+ "version": "1.0",
125
+ profile: {
126
+ "refresh_token": refresh_token,
127
+ "client_id": client_id,
128
+ "client_secret": client_secret,
129
+ "aws_access_key_id": aws_access_key_id,
130
+ "aws_secret_access_key": aws_secret_access_key,
131
+ "seller_id": seller_id,
132
+ "marketplace_id": marketplace_id,
133
+ },
134
+ }
135
+
136
+ # Merge with existing if present
137
+ if os.path.exists(path):
138
+ try:
139
+ with open(path, "r") as f:
140
+ existing = yaml.safe_load(f) or {}
141
+ existing[profile] = credentials[profile]
142
+ credentials = existing
143
+ click.echo(f"\n📝 Merged with existing credentials at {path}")
144
+ except Exception as e:
145
+ click.echo(f"⚠️ Could not read existing file: {e}")
146
+
147
+ os.makedirs(os.path.dirname(path), exist_ok=True)
148
+ with open(path, "w") as f:
149
+ yaml.dump(credentials, f, default_flow_style=False, sort_keys=False)
150
+
151
+ click.echo(f"✅ Credentials saved to {path}")
152
+ click.echo(f" Profile: {profile}")
153
+ click.echo(f" Seller ID: {seller_id}")
154
+ click.echo(f" Marketplace ID: {marketplace_id}")
155
+ click.echo()
156
+ click.echo("You can now use: python -m amazon_sp_cli.main --profile {profile} get-price <sku>")
157
+
158
+
159
+ @auth.command("show")
160
+ @click.option("--path", default=DEFAULT_CREDENTIALS_PATH, help="Path to credentials file")
161
+ @click.pass_context
162
+ def auth_show(ctx, path):
163
+ """Show configured profiles (without secrets)."""
164
+ if not os.path.exists(path):
165
+ click.echo(f"❌ No credentials file found at {path}")
166
+ click.echo("Run: python -m amazon_sp_cli.main auth setup")
167
+ return
168
+
169
+ with open(path, "r") as f:
170
+ creds = yaml.safe_load(f) or {}
171
+
172
+ click.echo(f"\n📄 Credentials file: {path}")
173
+ click.echo("-" * 40)
174
+
175
+ for profile, data in creds.items():
176
+ if profile == "version":
177
+ continue
178
+ click.echo(f"Profile: {profile}")
179
+ click.echo(f" Client ID: {data.get('client_id', 'N/A')[:20]}...")
180
+ click.echo(f" Seller ID: {data.get('seller_id', 'N/A')}")
181
+ click.echo(f" Marketplace ID: {data.get('marketplace_id', 'N/A')}")
182
+ click.echo()
183
+
184
+
65
185
  @cli.command()
66
186
  @click.argument("sku")
67
187
  @click.pass_context
@@ -189,43 +309,33 @@ def create_discount(ctx, sku, percent, all_variations):
189
309
  @click.argument("discount", type=float)
190
310
  @click.option(
191
311
  "--type",
192
- "coupon_type",
312
+ "discount_type",
193
313
  type=click.Choice(["percentage", "fixed"]),
194
314
  default="percentage",
195
- help="Coupon type: percentage or fixed amount off",
315
+ help="Discount type: percentage or fixed amount off",
196
316
  )
197
- @click.option("--min-purchase", type=float, default=0, help="Minimum purchase amount required")
198
317
  @click.option("--start-date", help="Start date (YYYY-MM-DD). Defaults to today")
199
318
  @click.option("--end-date", help="End date (YYYY-MM-DD). Defaults to 30 days from start")
200
- @click.option("--budget", type=float, default=500, help="Total coupon budget in USD (default: 500)")
201
- @click.option("--customer-budget", type=float, help="Maximum discount per customer (defaults to full discount)")
202
- @click.option("--prime-only", is_flag=True, help="Prime members only")
203
- @click.option("--clip-coupon", is_flag=True, default=True, help="Require customers to clip coupon (default: True)")
204
- @click.option("--output", "-o", type=click.File("w"), help="Save coupon data to file")
319
+ @click.option("--output", "-o", type=click.File("w"), help="Save price adjustment data to file")
205
320
  @click.pass_context
206
- def create_coupon(
321
+ def sale_price(
207
322
  ctx,
208
323
  sku,
209
324
  discount,
210
- coupon_type,
211
- min_purchase,
325
+ discount_type,
212
326
  start_date,
213
327
  end_date,
214
- budget,
215
- customer_budget,
216
- prime_only,
217
- clip_coupon,
218
328
  output,
219
329
  ):
220
- """Create a coupon for a SKU.
330
+ """Generate sale price data for a SKU.
221
331
 
222
- Since SP-API doesn't support direct coupon creation, this generates
223
- the coupon specification for use in Seller Central.
332
+ SP-API does not support direct sale price creation. This generates
333
+ the feed data you can submit via the Feeds API or use in Seller Central.
224
334
 
225
335
  Examples:
226
- amz-sp create-coupon PAW2603190101 20
227
- amz-sp create-coupon PAW2603190101 5 --type fixed --budget 1000
228
- amz-sp create-coupon PAW2603190101 15 --prime-only --start-date 2026-05-01
336
+ amz-sp sale-price PAW2603190101 20
337
+ amz-sp sale-price PAW2603190101 5 --type fixed
338
+ amz-sp sale-price PAW2603190101 15 --start-date 2026-05-01 --end-date 2026-05-31
229
339
  """
230
340
  client = ctx.obj["client"]
231
341
 
@@ -251,94 +361,69 @@ def create_coupon(
251
361
  end_str = end.strftime("%Y-%m-%dT%H:%M:%SZ")
252
362
 
253
363
  # Calculate discount values
254
- if coupon_type == "percentage":
364
+ if discount_type == "percentage":
255
365
  discount_amount = round(current_price * discount / 100, 2)
256
366
  discount_display = f"{discount}%"
257
367
  else:
258
368
  discount_amount = discount
259
369
  discount_display = f"${discount}"
260
370
 
261
- sale_price = max(0, current_price - discount_amount)
262
-
263
- # Customer budget defaults to discount amount if not specified
264
- per_customer = customer_budget or discount_amount
371
+ new_sale_price = max(0, current_price - discount_amount)
265
372
 
266
- # Build coupon specification
267
- coupon = {
268
- "coupon_specification": {
373
+ # Build sale price feed data
374
+ feed = {
375
+ "sku": sku,
376
+ "asin": response.get("summaries", [{}])[0].get("asin"),
377
+ "pricing": {
378
+ "original_price": current_price,
379
+ "sale_price": new_sale_price,
380
+ "discount_amount": discount_amount,
381
+ "discount_display": discount_display,
382
+ },
383
+ "schedule": {
384
+ "start_date": start_str,
385
+ "end_date": end_str,
386
+ "duration_days": (end - start).days,
387
+ },
388
+ "feed_data": {
389
+ "messageId": 1,
269
390
  "sku": sku,
270
- "asin": response.get("summaries", [{}])[0].get("asin"),
271
- "coupon_type": coupon_type,
272
- "discount": {
273
- "percentage" if coupon_type == "percentage" else "fixed_amount": discount,
274
- "display": discount_display,
391
+ "operationType": "PARTIAL_UPDATE",
392
+ "productType": "PET_TOY",
393
+ "attributes": {
394
+ "list_price": [{"currency": "USD", "value": current_price}],
395
+ "sale_price": [
396
+ {
397
+ "currency": "USD",
398
+ "value": new_sale_price,
399
+ "effective_date": start_str,
400
+ "end_date": end_str,
401
+ }
402
+ ],
275
403
  },
276
- "pricing": {
277
- "original_price": current_price,
278
- "discounted_price": sale_price,
279
- "savings": discount_amount,
280
- },
281
- "requirements": {
282
- "minimum_purchase": min_purchase if min_purchase > 0 else None,
283
- "prime_only": prime_only,
284
- "clip_required": clip_coupon,
285
- },
286
- "schedule": {
287
- "start_date": start_str,
288
- "end_date": end_str,
289
- "duration_days": (end - start).days,
290
- },
291
- "budget": {
292
- "total_budget": budget,
293
- "per_customer_max": per_customer,
294
- "estimated_redemptions": int(budget / discount_amount) if discount_amount > 0 else 0,
295
- },
296
- "status": "DRAFT",
297
404
  },
298
- "seller_central_steps": [
299
- "1. Go to Seller Central → Advertising → Coupons",
300
- "2. Click 'Create a new coupon'",
301
- "3. Search for the ASIN or SKU above",
302
- "4. Select discount type: " + ("Percentage Off" if coupon_type == "percentage" else "Money Off"),
303
- "5. Enter discount value: " + str(discount),
304
- "6. Set budget: $" + str(budget),
305
- "7. Set schedule: " + start_str + " to " + end_str,
306
- f"8. {'Enable Prime-only targeting' if prime_only else 'Target all customers'}",
307
- "9. Review and submit",
308
- ],
309
- "notes": [
310
- "Coupons typically take 4-8 hours to activate after submission",
311
- "Amazon charges $0.60 per redemption (US marketplace)",
312
- "Coupon will display on product detail page and search results",
313
- f"Estimated cost per redemption: ${discount_amount + 0.60:.2f} (discount + Amazon fee)",
314
- ],
315
405
  }
316
406
 
317
- # Remove None values
318
- if coupon["coupon_specification"]["requirements"]["minimum_purchase"] is None:
319
- del coupon["coupon_specification"]["requirements"]["minimum_purchase"]
320
-
321
407
  # Output
322
- output_json = json.dumps(coupon, indent=2)
408
+ output_json = json.dumps(feed, indent=2)
323
409
 
324
410
  if output:
325
411
  output.write(output_json)
326
- click.echo(f"✓ Coupon specification saved to {output.name}")
412
+ click.echo(f"✓ Sale price data saved to {output.name}")
327
413
 
328
414
  click.echo(output_json)
329
415
 
330
416
  # Summary
331
417
  click.echo("\n" + "=" * 50)
332
- click.echo("SUMMARY")
418
+ click.echo("SALE PRICE SUMMARY")
333
419
  click.echo("=" * 50)
334
420
  click.echo(f"SKU: {sku}")
335
421
  click.echo(f"Discount: {discount_display}")
336
- click.echo(f"Price: ${current_price} → ${sale_price}")
337
- click.echo(f"Budget: ${budget}")
422
+ click.echo(f"Price: ${current_price} → ${new_sale_price}")
338
423
  click.echo(f"Duration: {start_str} to {end_str}")
339
- click.echo(f"Prime Only: {'Yes' if prime_only else 'No'}")
340
- click.echo("\n⚠️ SP-API does not support direct coupon creation.")
341
- click.echo(" Use the Seller Central steps above to create the coupon.")
424
+ click.echo("\n⚠️ SP-API does not support direct sale price creation.")
425
+ click.echo(" Submit the feed_data above via the Feeds API")
426
+ click.echo(" or update manually in Seller Central.")
342
427
 
343
428
  except Exception as e:
344
429
  click.echo(f"Error: {e}", err=True)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: amazon-sp-cli
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: CLI tool for Amazon Selling Partner API (SP-API) operations
5
5
  Home-page: https://github.com/stellaraether/amazon-sp-cli
6
6
  Author: Lunan Li
@@ -17,4 +17,4 @@ amazon_sp_cli.egg-info/top_level.txt
17
17
  tests/__init__.py
18
18
  tests/test_auth.py
19
19
  tests/test_client.py
20
- tests/test_coupon.py
20
+ tests/test_sale_price.py
@@ -2,7 +2,7 @@ from setuptools import find_packages, setup
2
2
 
3
3
  setup(
4
4
  name="amazon-sp-cli",
5
- version="0.1.2",
5
+ version="0.1.4",
6
6
  description="CLI tool for Amazon Selling Partner API (SP-API) operations",
7
7
  author="Lunan Li",
8
8
  author_email="lunan@stellaraether.com",
@@ -1,4 +1,4 @@
1
- """Tests for coupon command."""
1
+ """Tests for sale-price command."""
2
2
 
3
3
  import json
4
4
  from unittest.mock import Mock, patch
@@ -9,8 +9,8 @@ from click.testing import CliRunner
9
9
  from amazon_sp_cli.main import cli
10
10
 
11
11
 
12
- class TestCreateCoupon:
13
- """Test create-coupon command."""
12
+ class TestSalePrice:
13
+ """Test sale-price command."""
14
14
 
15
15
  @pytest.fixture
16
16
  def mock_listing_response(self):
@@ -27,8 +27,8 @@ class TestCreateCoupon:
27
27
 
28
28
  @patch("amazon_sp_cli.main.SPAPIAuth")
29
29
  @patch("amazon_sp_cli.main.SPAPIClient")
30
- def test_create_coupon_percentage(self, mock_client_class, mock_auth_class, runner, mock_listing_response):
31
- """Test creating a percentage coupon."""
30
+ def test_sale_price_percentage(self, mock_client_class, mock_auth_class, runner, mock_listing_response):
31
+ """Test generating percentage sale price."""
32
32
  mock_client = Mock()
33
33
  mock_client.get_listing.return_value = mock_listing_response
34
34
  mock_client_class.return_value = mock_client
@@ -36,18 +36,18 @@ class TestCreateCoupon:
36
36
  mock_auth = Mock()
37
37
  mock_auth_class.return_value = mock_auth
38
38
 
39
- result = runner.invoke(cli, ["create-coupon", "TEST-SKU", "20"])
39
+ result = runner.invoke(cli, ["sale-price", "TEST-SKU", "20"])
40
40
 
41
41
  assert result.exit_code == 0
42
42
  assert "20.0%" in result.output
43
43
  assert "$29.99" in result.output
44
- assert "$23.99" in result.output # 20% off
45
- assert "Seller Central" in result.output
44
+ assert "$23.99" in result.output
45
+ assert "feed_data" in result.output
46
46
 
47
47
  @patch("amazon_sp_cli.main.SPAPIAuth")
48
48
  @patch("amazon_sp_cli.main.SPAPIClient")
49
- def test_create_coupon_fixed(self, mock_client_class, mock_auth_class, runner, mock_listing_response):
50
- """Test creating a fixed amount coupon."""
49
+ def test_sale_price_fixed(self, mock_client_class, mock_auth_class, runner, mock_listing_response):
50
+ """Test generating fixed amount sale price."""
51
51
  mock_client = Mock()
52
52
  mock_client.get_listing.return_value = mock_listing_response
53
53
  mock_client_class.return_value = mock_client
@@ -55,16 +55,16 @@ class TestCreateCoupon:
55
55
  mock_auth = Mock()
56
56
  mock_auth_class.return_value = mock_auth
57
57
 
58
- result = runner.invoke(cli, ["create-coupon", "TEST-SKU", "5", "--type", "fixed"])
58
+ result = runner.invoke(cli, ["sale-price", "TEST-SKU", "5", "--type", "fixed"])
59
59
 
60
60
  assert result.exit_code == 0
61
61
  assert "$5" in result.output
62
- assert "$24.99" in result.output # $29.99 - $5
62
+ assert "$24.99" in result.output
63
63
 
64
64
  @patch("amazon_sp_cli.main.SPAPIAuth")
65
65
  @patch("amazon_sp_cli.main.SPAPIClient")
66
- def test_create_coupon_with_options(self, mock_client_class, mock_auth_class, runner, mock_listing_response):
67
- """Test creating coupon with all options."""
66
+ def test_sale_price_with_dates(self, mock_client_class, mock_auth_class, runner, mock_listing_response):
67
+ """Test generating sale price with custom dates."""
68
68
  mock_client = Mock()
69
69
  mock_client.get_listing.return_value = mock_listing_response
70
70
  mock_client_class.return_value = mock_client
@@ -75,12 +75,9 @@ class TestCreateCoupon:
75
75
  result = runner.invoke(
76
76
  cli,
77
77
  [
78
- "create-coupon",
78
+ "sale-price",
79
79
  "TEST-SKU",
80
80
  "15",
81
- "--prime-only",
82
- "--budget",
83
- "1000",
84
81
  "--start-date",
85
82
  "2026-05-01",
86
83
  "--end-date",
@@ -89,15 +86,13 @@ class TestCreateCoupon:
89
86
  )
90
87
 
91
88
  assert result.exit_code == 0
92
- assert "Prime Only: Yes" in result.output
93
- assert "$1000" in result.output
94
89
  assert "2026-05-01" in result.output
95
90
  assert "2026-06-01" in result.output
96
91
 
97
92
  @patch("amazon_sp_cli.main.SPAPIAuth")
98
93
  @patch("amazon_sp_cli.main.SPAPIClient")
99
- def test_create_coupon_output_file(self, mock_client_class, mock_auth_class, runner, mock_listing_response):
100
- """Test saving coupon to file."""
94
+ def test_sale_price_output_file(self, mock_client_class, mock_auth_class, runner, mock_listing_response):
95
+ """Test saving sale price data to file."""
101
96
  mock_client = Mock()
102
97
  mock_client.get_listing.return_value = mock_listing_response
103
98
  mock_client_class.return_value = mock_client
@@ -106,20 +101,20 @@ class TestCreateCoupon:
106
101
  mock_auth_class.return_value = mock_auth
107
102
 
108
103
  with runner.isolated_filesystem():
109
- result = runner.invoke(cli, ["create-coupon", "TEST-SKU", "10", "-o", "coupon.json"])
104
+ result = runner.invoke(cli, ["sale-price", "TEST-SKU", "10", "-o", "sale-price.json"])
110
105
 
111
106
  assert result.exit_code == 0
112
107
  assert "saved to" in result.output
113
108
 
114
109
  # Verify file was created
115
- with open("coupon.json") as f:
110
+ with open("sale-price.json") as f:
116
111
  data = json.load(f)
117
- assert data["coupon_specification"]["sku"] == "TEST-SKU"
118
- assert data["coupon_specification"]["coupon_type"] == "percentage"
112
+ assert data["sku"] == "TEST-SKU"
113
+ assert data["pricing"]["discount_display"] == "10.0%"
119
114
 
120
115
  @patch("amazon_sp_cli.main.SPAPIAuth")
121
116
  @patch("amazon_sp_cli.main.SPAPIClient")
122
- def test_create_coupon_no_price(self, mock_client_class, mock_auth_class, runner):
117
+ def test_sale_price_no_price(self, mock_client_class, mock_auth_class, runner):
123
118
  """Test error when listing has no price."""
124
119
  mock_client = Mock()
125
120
  mock_client.get_listing.return_value = {
@@ -131,7 +126,7 @@ class TestCreateCoupon:
131
126
  mock_auth = Mock()
132
127
  mock_auth_class.return_value = mock_auth
133
128
 
134
- result = runner.invoke(cli, ["create-coupon", "TEST-SKU", "20"])
129
+ result = runner.invoke(cli, ["sale-price", "TEST-SKU", "20"])
135
130
 
136
131
  assert result.exit_code != 0
137
132
  assert "Error" in result.output
File without changes
File without changes
File without changes
File without changes