When a customer makes a purchase on Etsy, the system creates several related objects:
For digital products, orders are processed immediately since there’s no shipping delay.
Receipt (Order)
├── receipt_id: Unique order identifier
├── buyer_user_id: Customer's user ID
├── buyer_email: Customer's email (with permission)
├── create_timestamp: When order was placed
├── payment_method: How customer paid
├── transactions: List of items purchased
│ ├── Transaction 1
│ │ ├── listing_id: Product purchased
│ │ ├── quantity: Number purchased
│ │ ├── price: Price per item
│ │ └── shipping_cost: (0 for digital)
│ └── Transaction 2...
├── subtotal: Order subtotal
├── total_price: Final price
├── discount_amt: Any discounts applied
└── status: Order status
# etsy_client/orders.py
"""Order and transaction operations for Etsy API."""
from typing import Dict, Any, Optional, List
from datetime import datetime, timedelta
from enum import Enum
from dataclasses import dataclass
from .client import EtsyClient
class ReceiptStatus(Enum):
"""Possible receipt statuses."""
PAID = "paid"
COMPLETED = "completed"
OPEN = "open"
PAYMENT_PROCESSING = "payment_processing"
CANCELLED = "cancelled"
@dataclass
class OrderSummary:
"""Summary of an order."""
receipt_id: int
buyer_name: str
total: float
currency: str
items: List[Dict]
created_at: datetime
status: str
is_digital: bool
@property
def item_count(self) -> int:
return sum(item.get('quantity', 1) for item in self.items)
class OrderOperations:
"""Operations for managing Etsy orders."""
def __init__(self, client: EtsyClient):
self.client = client
# ==================== RECEIPTS (ORDERS) ====================
def get_receipt(
self,
shop_id: int,
receipt_id: int
) -> Dict[str, Any]:
"""
Get a specific receipt/order.
Args:
shop_id: The shop ID
receipt_id: The receipt ID
Returns:
Receipt data with transactions
"""
return self.client.get(
f"/application/shops/{shop_id}/receipts/{receipt_id}"
)
def get_shop_receipts(
self,
shop_id: int,
min_created: Optional[datetime] = None,
max_created: Optional[datetime] = None,
was_paid: bool = True,
limit: int = 100,
offset: int = 0
) -> Dict[str, Any]:
"""
Get receipts for a shop.
Args:
shop_id: The shop ID
min_created: Filter by minimum creation date
max_created: Filter by maximum creation date
was_paid: Filter for paid orders only
limit: Results per page (max 100)
offset: Pagination offset
Returns:
List of receipts
"""
params = {
"limit": limit,
"offset": offset,
"was_paid": was_paid
}
if min_created:
params["min_created"] = int(min_created.timestamp())
if max_created:
params["max_created"] = int(max_created.timestamp())
return self.client.get(
f"/application/shops/{shop_id}/receipts",
params=params
)
def get_all_receipts(
self,
shop_id: int,
min_created: Optional[datetime] = None,
max_created: Optional[datetime] = None,
was_paid: bool = True
) -> List[Dict[str, Any]]:
"""
Get ALL receipts, handling pagination.
Args:
shop_id: The shop ID
min_created: Filter by minimum creation date
max_created: Filter by maximum creation date
was_paid: Filter for paid orders only
Returns:
List of all receipts
"""
all_receipts = []
offset = 0
limit = 100
while True:
response = self.get_shop_receipts(
shop_id, min_created, max_created, was_paid, limit, offset
)
receipts = response.get('results', [])
all_receipts.extend(receipts)
if len(receipts) < limit:
break
offset += limit
return all_receipts
def get_recent_orders(
self,
shop_id: int,
days: int = 30
) -> List[Dict[str, Any]]:
"""
Get orders from the last N days.
Args:
shop_id: The shop ID
days: Number of days to look back
Returns:
List of recent receipts
"""
min_date = datetime.now() - timedelta(days=days)
return self.get_all_receipts(shop_id, min_created=min_date)
# ==================== TRANSACTIONS ====================
def get_shop_transactions(
self,
shop_id: int,
limit: int = 100,
offset: int = 0
) -> Dict[str, Any]:
"""
Get transactions (individual sales) for a shop.
Args:
shop_id: The shop ID
limit: Results per page
offset: Pagination offset
Returns:
List of transactions
"""
return self.client.get(
f"/application/shops/{shop_id}/transactions",
params={"limit": limit, "offset": offset}
)
def get_transaction(
self,
shop_id: int,
transaction_id: int
) -> Dict[str, Any]:
"""
Get a specific transaction.
Args:
shop_id: The shop ID
transaction_id: The transaction ID
Returns:
Transaction data
"""
return self.client.get(
f"/application/shops/{shop_id}/transactions/{transaction_id}"
)
def get_listing_transactions(
self,
shop_id: int,
listing_id: int,
limit: int = 100
) -> Dict[str, Any]:
"""
Get all transactions for a specific listing.
Args:
shop_id: The shop ID
listing_id: The listing ID
limit: Maximum results
Returns:
List of transactions for the listing
"""
return self.client.get(
f"/application/shops/{shop_id}/listings/{listing_id}/transactions",
params={"limit": limit}
)
# ==================== HELPER METHODS ====================
def parse_receipt(self, receipt: Dict[str, Any]) -> OrderSummary:
"""
Parse a receipt into a structured OrderSummary.
Args:
receipt: Raw receipt data from API
Returns:
OrderSummary object
"""
transactions = receipt.get('transactions', [])
items = []
is_digital = True
for txn in transactions:
items.append({
'listing_id': txn.get('listing_id'),
'title': txn.get('title'),
'quantity': txn.get('quantity', 1),
'price': txn.get('price', {}).get('amount', 0) /
txn.get('price', {}).get('divisor', 100)
})
if not txn.get('is_digital', False):
is_digital = False
total_price = receipt.get('grandtotal', {})
total = total_price.get('amount', 0) / total_price.get('divisor', 100)
return OrderSummary(
receipt_id=receipt['receipt_id'],
buyer_name=receipt.get('name', 'Unknown'),
total=total,
currency=total_price.get('currency_code', 'USD'),
items=items,
created_at=datetime.fromtimestamp(
receipt.get('create_timestamp', 0)
),
status=receipt.get('status', 'unknown'),
is_digital=is_digital
)
def get_digital_orders(
self,
shop_id: int,
days: int = 30
) -> List[OrderSummary]:
"""
Get only digital product orders.
Args:
shop_id: The shop ID
days: Number of days to look back
Returns:
List of digital-only OrderSummary objects
"""
receipts = self.get_recent_orders(shop_id, days)
digital_orders = []
for receipt in receipts:
summary = self.parse_receipt(receipt)
if summary.is_digital:
digital_orders.append(summary)
return digital_orders
class TransactionAnalyzer:
"""Analyze transaction data for insights."""
def __init__(self, transactions: List[Dict[str, Any]]):
self.transactions = transactions
def total_revenue(self) -> float:
"""Calculate total revenue from transactions."""
total = 0
for txn in self.transactions:
price = txn.get('price', {})
amount = price.get('amount', 0)
divisor = price.get('divisor', 100)
quantity = txn.get('quantity', 1)
total += (amount / divisor) * quantity
return total
def sales_by_listing(self) -> Dict[int, Dict]:
"""Group sales by listing."""
by_listing = {}
for txn in self.transactions:
listing_id = txn.get('listing_id')
if listing_id not in by_listing:
by_listing[listing_id] = {
'title': txn.get('title', 'Unknown'),
'quantity': 0,
'revenue': 0
}
price = txn.get('price', {})
amount = price.get('amount', 0) / price.get('divisor', 100)
quantity = txn.get('quantity', 1)
by_listing[listing_id]['quantity'] += quantity
by_listing[listing_id]['revenue'] += amount * quantity
return by_listing
def top_sellers(self, limit: int = 10) -> List[Dict]:
"""Get top selling products."""
by_listing = self.sales_by_listing()
sorted_listings = sorted(
by_listing.items(),
key=lambda x: x[1]['revenue'],
reverse=True
)
return [
{'listing_id': lid, **data}
for lid, data in sorted_listings[:limit]
]
def daily_revenue(self) -> Dict[str, float]:
"""Calculate revenue by day."""
by_day = {}
for txn in self.transactions:
timestamp = txn.get('created_timestamp', 0)
date_str = datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d')
price = txn.get('price', {})
amount = price.get('amount', 0) / price.get('divisor', 100)
quantity = txn.get('quantity', 1)
if date_str not in by_day:
by_day[date_str] = 0
by_day[date_str] += amount * quantity
return dict(sorted(by_day.items()))
# scripts/daily_orders.py
"""Generate daily orders report."""
import sys
from pathlib import Path
from datetime import datetime, timedelta
sys.path.insert(0, str(Path(__file__).parent.parent))
from config import Config
from etsy_client import EtsyClient
from etsy_client.orders import OrderOperations, TransactionAnalyzer
from etsy_client.users import get_my_shop_id
def generate_daily_report(days: int = 1):
"""
Generate a report of orders from the last N days.
Args:
days: Number of days to include
"""
client = EtsyClient(
api_key=Config.ETSY_API_KEY,
access_token=Config.ETSY_ACCESS_TOKEN
)
shop_id = get_my_shop_id(client)
order_ops = OrderOperations(client)
# Get recent orders
print(f"Fetching orders from the last {days} day(s)...")
min_date = datetime.now() - timedelta(days=days)
receipts = order_ops.get_all_receipts(shop_id, min_created=min_date)
print(f"\n{'='*60}")
print(f"DAILY ORDERS REPORT")
print(f"Period: {min_date.strftime('%Y-%m-%d')} to {datetime.now().strftime('%Y-%m-%d')}")
print(f"{'='*60}\n")
if not receipts:
print("No orders in this period.")
return
# Process orders
total_revenue = 0
digital_count = 0
print(f"{'Order ID':<12} {'Date':<12} {'Customer':<20} {'Total':>10} {'Type':<10}")
print("-" * 70)
for receipt in receipts:
summary = order_ops.parse_receipt(receipt)
total_revenue += summary.total
if summary.is_digital:
digital_count += 1
order_type = "Digital" if summary.is_digital else "Physical"
print(
f"{summary.receipt_id:<12} "
f"{summary.created_at.strftime('%Y-%m-%d'):<12} "
f"{summary.buyer_name[:18]:<20} "
f"${summary.total:>9.2f} "
f"{order_type:<10}"
)
print("-" * 70)
print(f"\nSUMMARY:")
print(f" Total Orders: {len(receipts)}")
print(f" Digital Orders: {digital_count}")
print(f" Physical Orders: {len(receipts) - digital_count}")
print(f" Total Revenue: ${total_revenue:.2f}")
print(f" Average Order Value: ${total_revenue / len(receipts):.2f}")
def export_orders_csv(days: int, output_file: str):
"""
Export orders to CSV file.
Args:
days: Number of days to include
output_file: Output CSV file path
"""
import csv
client = EtsyClient(
api_key=Config.ETSY_API_KEY,
access_token=Config.ETSY_ACCESS_TOKEN
)
shop_id = get_my_shop_id(client)
order_ops = OrderOperations(client)
min_date = datetime.now() - timedelta(days=days)
receipts = order_ops.get_all_receipts(shop_id, min_created=min_date)
with open(output_file, 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
# Header
writer.writerow([
'Receipt ID', 'Date', 'Customer', 'Email',
'Items', 'Subtotal', 'Discount', 'Total',
'Currency', 'Digital', 'Status'
])
for receipt in receipts:
summary = order_ops.parse_receipt(receipt)
writer.writerow([
summary.receipt_id,
summary.created_at.strftime('%Y-%m-%d %H:%M:%S'),
summary.buyer_name,
receipt.get('buyer_email', ''),
summary.item_count,
receipt.get('subtotal', {}).get('amount', 0) / 100,
receipt.get('discount_amt', {}).get('amount', 0) / 100,
summary.total,
summary.currency,
'Yes' if summary.is_digital else 'No',
summary.status
])
print(f"✓ Exported {len(receipts)} orders to {output_file}")
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description='Generate orders report')
parser.add_argument('--days', type=int, default=1, help='Days to include')
parser.add_argument('--export', type=str, help='Export to CSV file')
args = parser.parse_args()
if args.export:
export_orders_csv(args.days, args.export)
else:
generate_daily_report(args.days)
# scripts/best_sellers.py
"""Analyze best-selling products."""
import sys
from pathlib import Path
from datetime import datetime, timedelta
sys.path.insert(0, str(Path(__file__).parent.parent))
from config import Config
from etsy_client import EtsyClient
from etsy_client.orders import OrderOperations, TransactionAnalyzer
from etsy_client.users import get_my_shop_id
def analyze_best_sellers(days: int = 30, limit: int = 10):
"""
Analyze best-selling products.
Args:
days: Period to analyze
limit: Number of top products to show
"""
client = EtsyClient(
api_key=Config.ETSY_API_KEY,
access_token=Config.ETSY_ACCESS_TOKEN
)
shop_id = get_my_shop_id(client)
order_ops = OrderOperations(client)
print(f"Analyzing sales from the last {days} days...\n")
# Get all receipts
min_date = datetime.now() - timedelta(days=days)
receipts = order_ops.get_all_receipts(shop_id, min_created=min_date)
# Extract all transactions
all_transactions = []
for receipt in receipts:
transactions = receipt.get('transactions', [])
all_transactions.extend(transactions)
if not all_transactions:
print("No transactions found in this period.")
return
# Analyze
analyzer = TransactionAnalyzer(all_transactions)
# Top sellers
top = analyzer.top_sellers(limit)
print(f"{'='*70}")
print(f"TOP {limit} BEST SELLERS")
print(f"Period: Last {days} days | Total Transactions: {len(all_transactions)}")
print(f"{'='*70}\n")
print(f"{'Rank':<6} {'Product':<35} {'Sales':>8} {'Revenue':>12}")
print("-" * 65)
for i, product in enumerate(top, 1):
title = product['title'][:33] if product['title'] else 'Unknown'
print(
f"{i:<6} {title:<35} "
f"{product['quantity']:>8} "
f"${product['revenue']:>11.2f}"
)
print("-" * 65)
# Summary stats
total_revenue = analyzer.total_revenue()
top_revenue = sum(p['revenue'] for p in top)
print(f"\nSUMMARY:")
print(f" Total Revenue: ${total_revenue:.2f}")
print(f" Top {limit} Revenue: ${top_revenue:.2f} ({top_revenue/total_revenue*100:.1f}%)")
print(f" Unique Products Sold: {len(analyzer.sales_by_listing())}")
def revenue_trends(days: int = 30):
"""
Analyze revenue trends over time.
Args:
days: Period to analyze
"""
client = EtsyClient(
api_key=Config.ETSY_API_KEY,
access_token=Config.ETSY_ACCESS_TOKEN
)
shop_id = get_my_shop_id(client)
order_ops = OrderOperations(client)
min_date = datetime.now() - timedelta(days=days)
receipts = order_ops.get_all_receipts(shop_id, min_created=min_date)
all_transactions = []
for receipt in receipts:
all_transactions.extend(receipt.get('transactions', []))
analyzer = TransactionAnalyzer(all_transactions)
daily = analyzer.daily_revenue()
print(f"\n{'='*50}")
print("DAILY REVENUE TREND")
print(f"{'='*50}\n")
print(f"{'Date':<12} {'Revenue':>12} {'Graph'}")
print("-" * 50)
max_revenue = max(daily.values()) if daily else 1
for date, revenue in daily.items():
bar_length = int((revenue / max_revenue) * 30)
bar = "█" * bar_length
print(f"{date:<12} ${revenue:>10.2f} {bar}")
print("-" * 50)
print(f"\nTotal: ${sum(daily.values()):.2f}")
print(f"Average: ${sum(daily.values()) / len(daily):.2f}/day" if daily else "")
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description='Analyze best sellers')
parser.add_argument('--days', type=int, default=30, help='Days to analyze')
parser.add_argument('--limit', type=int, default=10, help='Number of products')
parser.add_argument('--trends', action='store_true', help='Show revenue trends')
args = parser.parse_args()
if args.trends:
revenue_trends(args.days)
else:
analyze_best_sellers(args.days, args.limit)
# etsy_client/notifications.py
"""Order notification handling."""
from typing import Dict, Any, Callable, List
from datetime import datetime
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from .client import EtsyClient
from .orders import OrderOperations, OrderSummary
class OrderNotifier:
"""Send notifications for new orders."""
def __init__(
self,
client: EtsyClient,
shop_id: int,
smtp_config: Dict[str, str] = None
):
"""
Initialize notifier.
Args:
client: EtsyClient instance
shop_id: Shop ID to monitor
smtp_config: Email configuration (server, port, user, password)
"""
self.client = client
self.shop_id = shop_id
self.order_ops = OrderOperations(client)
self.smtp_config = smtp_config
self._callbacks: List[Callable[[OrderSummary], None]] = []
self._last_check: datetime = datetime.now()
self._known_orders: set = set()
def register_callback(
self,
callback: Callable[[OrderSummary], None]
):
"""Register a callback for new orders."""
self._callbacks.append(callback)
def check_new_orders(self) -> List[OrderSummary]:
"""
Check for new orders since last check.
Returns:
List of new orders
"""
receipts = self.order_ops.get_shop_receipts(
self.shop_id,
min_created=self._last_check
)
new_orders = []
for receipt in receipts.get('results', []):
receipt_id = receipt['receipt_id']
if receipt_id not in self._known_orders:
self._known_orders.add(receipt_id)
summary = self.order_ops.parse_receipt(receipt)
new_orders.append(summary)
# Trigger callbacks
for callback in self._callbacks:
try:
callback(summary)
except Exception as e:
print(f"Callback error: {e}")
self._last_check = datetime.now()
return new_orders
def send_email_notification(
self,
order: OrderSummary,
recipient: str
):
"""
Send email notification for an order.
Args:
order: Order summary
recipient: Email address to notify
"""
if not self.smtp_config:
raise ValueError("SMTP not configured")
# Create email
msg = MIMEMultipart('alternative')
msg['Subject'] = f"New Etsy Order #{order.receipt_id}"
msg['From'] = self.smtp_config['user']
msg['To'] = recipient
# Text content
text = f"""
New order received!
Order ID: {order.receipt_id}
Customer: {order.buyer_name}
Total: ${order.total:.2f} {order.currency}
Type: {'Digital' if order.is_digital else 'Physical'}
Items:
"""
for item in order.items:
text += f" - {item['title']} (x{item['quantity']}) - ${item['price']:.2f}\n"
# HTML content
html = f"""
<html>
<body>
<h2>New Order #{order.receipt_id}</h2>
<p><strong>Customer:</strong> {order.buyer_name}</p>
<p><strong>Total:</strong> ${order.total:.2f} {order.currency}</p>
<p><strong>Type:</strong> {'Digital' if order.is_digital else 'Physical'}</p>
<h3>Items:</h3>
<ul>
"""
for item in order.items:
html += f"<li>{item['title']} (x{item['quantity']}) - ${item['price']:.2f}</li>\n"
html += "</ul></body></html>"
msg.attach(MIMEText(text, 'plain'))
msg.attach(MIMEText(html, 'html'))
# Send
with smtplib.SMTP(
self.smtp_config['server'],
self.smtp_config['port']
) as server:
server.starttls()
server.login(
self.smtp_config['user'],
self.smtp_config['password']
)
server.send_message(msg)
def format_slack_message(self, order: OrderSummary) -> Dict:
"""
Format order as Slack message.
Args:
order: Order summary
Returns:
Slack message payload
"""
items_text = "\n".join(
f"• {item['title']} (x{item['quantity']})"
for item in order.items
)
return {
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": f"🎉 New Order #{order.receipt_id}"
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": f"*Customer:*\n{order.buyer_name}"
},
{
"type": "mrkdwn",
"text": f"*Total:*\n${order.total:.2f} {order.currency}"
}
]
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": f"*Items:*\n{items_text}"
}
}
]
}
# etsy_client/revenue.py
"""Revenue tracking and reporting."""
from typing import Dict, Any, List, Optional
from datetime import datetime, timedelta
from dataclasses import dataclass
from collections import defaultdict
from .client import EtsyClient
from .orders import OrderOperations
@dataclass
class RevenuePeriod:
"""Revenue data for a period."""
start_date: datetime
end_date: datetime
gross_revenue: float
etsy_fees: float
net_revenue: float
order_count: int
transaction_count: int
@property
def average_order_value(self) -> float:
return self.gross_revenue / self.order_count if self.order_count else 0
@property
def fee_percentage(self) -> float:
return (self.etsy_fees / self.gross_revenue * 100) if self.gross_revenue else 0
class RevenueTracker:
"""Track and analyze revenue."""
# Estimated Etsy fee structure (simplified)
LISTING_FEE = 0.20 # Per listing
TRANSACTION_FEE_PERCENT = 0.065 # 6.5% of sale
PAYMENT_PROCESSING_PERCENT = 0.03 # ~3% + fixed
PAYMENT_PROCESSING_FIXED = 0.25 # Per transaction
def __init__(self, client: EtsyClient, shop_id: int):
self.client = client
self.shop_id = shop_id
self.order_ops = OrderOperations(client)
def calculate_fees(
self,
sale_amount: float,
is_digital: bool = True
) -> float:
"""
Estimate Etsy fees for a sale.
Args:
sale_amount: Sale amount
is_digital: Whether this is a digital product
Returns:
Estimated total fees
"""
# Transaction fee
transaction_fee = sale_amount * self.TRANSACTION_FEE_PERCENT
# Payment processing
payment_fee = (sale_amount * self.PAYMENT_PROCESSING_PERCENT) + \
self.PAYMENT_PROCESSING_FIXED
# No listing renewal for digital products if auto-renew
listing_fee = 0 if is_digital else self.LISTING_FEE
return transaction_fee + payment_fee + listing_fee
def get_period_revenue(
self,
start_date: datetime,
end_date: datetime
) -> RevenuePeriod:
"""
Calculate revenue for a specific period.
Args:
start_date: Period start
end_date: Period end
Returns:
RevenuePeriod with calculated metrics
"""
receipts = self.order_ops.get_all_receipts(
self.shop_id,
min_created=start_date,
max_created=end_date
)
gross_revenue = 0
total_fees = 0
transaction_count = 0
for receipt in receipts:
# Get total from receipt
total_price = receipt.get('grandtotal', {})
amount = total_price.get('amount', 0) / total_price.get('divisor', 100)
gross_revenue += amount
# Count transactions
transactions = receipt.get('transactions', [])
transaction_count += len(transactions)
# Estimate fees
is_digital = all(
t.get('is_digital', False) for t in transactions
)
total_fees += self.calculate_fees(amount, is_digital)
return RevenuePeriod(
start_date=start_date,
end_date=end_date,
gross_revenue=gross_revenue,
etsy_fees=total_fees,
net_revenue=gross_revenue - total_fees,
order_count=len(receipts),
transaction_count=transaction_count
)
def get_monthly_revenue(
self,
year: int,
month: int
) -> RevenuePeriod:
"""Get revenue for a specific month."""
start = datetime(year, month, 1)
if month == 12:
end = datetime(year + 1, 1, 1)
else:
end = datetime(year, month + 1, 1)
return self.get_period_revenue(start, end)
def get_yearly_summary(
self,
year: int
) -> Dict[str, RevenuePeriod]:
"""
Get monthly revenue for an entire year.
Args:
year: Year to analyze
Returns:
Dictionary with month names as keys
"""
months = {}
for month in range(1, 13):
month_name = datetime(year, month, 1).strftime('%B')
# Skip future months
if datetime(year, month, 1) > datetime.now():
break
months[month_name] = self.get_monthly_revenue(year, month)
return months
def print_revenue_report(
self,
period: RevenuePeriod
):
"""Print a formatted revenue report."""
print(f"\n{'='*50}")
print("REVENUE REPORT")
print(f"Period: {period.start_date.strftime('%Y-%m-%d')} to "
f"{period.end_date.strftime('%Y-%m-%d')}")
print(f"{'='*50}\n")
print(f"{'Gross Revenue:':<25} ${period.gross_revenue:>12.2f}")
print(f"{'Estimated Etsy Fees:':<25} ${period.etsy_fees:>12.2f} "
f"({period.fee_percentage:.1f}%)")
print(f"{'Net Revenue:':<25} ${period.net_revenue:>12.2f}")
print()
print(f"{'Orders:':<25} {period.order_count:>12}")
print(f"{'Items Sold:':<25} {period.transaction_count:>12}")
print(f"{'Avg Order Value:':<25} ${period.average_order_value:>12.2f}")
With order processing understood, you’re ready to build automation around inventory and pricing. The next chapter covers dynamic pricing strategies and inventory management for digital products.
| ← Chapter 6: Digital Downloads and File Management | Table of Contents | Chapter 8: Inventory and Pricing Automation → |