fix ld data on offering pages

This commit is contained in:
Tobias Brunner 2025-07-16 16:47:21 +02:00
parent 529fc9148a
commit 1d39e3445e
Signed by: tobru
SSH key fingerprint: SHA256:kOXg1R6c11XW3/Pt9dbLdQvOJGFAy+B2K6v6PtRWBGQ

View file

@ -217,87 +217,78 @@ def json_ld_structured_data(context):
offering = context["offering"] offering = context["offering"]
offering_url = request.build_absolute_uri() offering_url = request.build_absolute_uri()
data = { # Check if we have pricing data available
"@context": "https://schema.org", has_pricing_data = False
"@type": "Product",
"name": f"Managed {offering.service.name} on {offering.cloud_provider.name}",
"description": offering.description or offering.service.description,
"url": offering_url,
"category": "Cloud Service",
}
# Add brand (service)
data["brand"] = {"@type": "Brand", "name": offering.service.name}
# Add image if available
if hasattr(offering.service, "get_logo") and offering.service.get_logo:
data["image"] = request.build_absolute_uri(offering.service.get_logo.url)
# Add offers if available
if hasattr(offering, "plans") and offering.plans.exists(): if hasattr(offering, "plans") and offering.plans.exists():
# Get all plans with pricing # Get all plans with pricing
plans_with_prices = offering.plans.filter( plans_with_prices = offering.plans.filter(
plan_prices__isnull=False plan_prices__isnull=False
).distinct() ).distinct()
has_pricing_data = plans_with_prices.exists()
if plans_with_prices.exists(): if has_pricing_data:
# Create individual offers for each plan # Use Product type with complete pricing information
offers = [] data = {
all_prices = [] "@context": "https://schema.org",
"@type": "Product",
"name": f"Managed {offering.service.name} on {offering.cloud_provider.name}",
"description": offering.description or offering.service.description,
"url": offering_url,
"category": "Cloud Service",
}
for plan in plans_with_prices: # Add brand (service)
plan_prices = plan.plan_prices.all() data["brand"] = {"@type": "Brand", "name": offering.service.name}
if plan_prices.exists():
first_price = plan_prices.first()
all_prices.extend([p.amount for p in plan_prices])
offer = { # Add image if available
"@type": "Offer", if hasattr(offering.service, "get_logo") and offering.service.get_logo:
"name": plan.name, data["image"] = request.build_absolute_uri(offering.service.get_logo.url)
"price": str(first_price.amount),
"priceCurrency": first_price.currency, # Create individual offers for each plan with pricing
"availability": "https://schema.org/InStock", offers = []
"url": offering_url + "#plan-order-form", all_prices = []
"seller": {"@type": "Organization", "name": "VSHN"},
} for plan in plans_with_prices:
offers.append(offer) plan_prices = plan.plan_prices.all()
if plan_prices.exists():
first_price = plan_prices.first()
all_prices.extend([p.amount for p in plan_prices])
offer = {
"@type": "Offer",
"name": plan.name,
"price": str(first_price.amount),
"priceCurrency": first_price.currency,
"availability": "https://schema.org/InStock",
"url": offering_url + "#plan-order-form",
"seller": {"@type": "Organization", "name": "VSHN"},
}
offers.append(offer)
# Add aggregate offer with all required pricing fields
if all_prices and offers:
# Use the currency from the first plan's first price
first_plan_with_prices = plans_with_prices.first()
first_currency = first_plan_with_prices.plan_prices.first().currency
# Add aggregate offer with all individual offers
data["offers"] = { data["offers"] = {
"@type": "AggregateOffer", "@type": "AggregateOffer",
"availability": "https://schema.org/InStock", "availability": "https://schema.org/InStock",
"offerCount": len(offers), "offerCount": len(offers),
"offers": offers, "offers": offers,
"lowPrice": str(min(all_prices)),
"highPrice": str(max(all_prices)),
"priceCurrency": first_currency,
"seller": {"@type": "Organization", "name": "VSHN"}, "seller": {"@type": "Organization", "name": "VSHN"},
} }
# Add lowPrice, highPrice and priceCurrency if we have prices
if all_prices:
data["offers"]["lowPrice"] = str(min(all_prices))
data["offers"]["highPrice"] = str(max(all_prices))
# Use the currency from the first plan's first price
first_plan_with_prices = plans_with_prices.first()
first_currency = first_plan_with_prices.plan_prices.first().currency
data["offers"]["priceCurrency"] = first_currency
# Note: aggregateRating and review fields are not included as this is a B2B # Note: aggregateRating and review fields are not included as this is a B2B
# service marketplace without a review system. These could be added in the future # service marketplace without a review system. These could be added in the future
# if customer reviews/ratings are implemented. # if customer reviews/ratings are implemented.
# Example structure for future implementation: else:
# if hasattr(offering, 'reviews') and offering.reviews.exists(): # No pricing data available - use Organization data instead of Product
# data["aggregateRating"] = { # to avoid Google Search Console errors for missing required Product fields
# "@type": "AggregateRating", data = organization_data
# "ratingValue": "4.5",
# "reviewCount": "10"
# }
else:
# No pricing available, just basic offer info
data["offers"] = {
"@type": "AggregateOffer",
"availability": "https://schema.org/InStock",
"offerCount": offering.plans.count(),
"seller": {"@type": "Organization", "name": "VSHN"},
}
elif view_name == "article_list": elif view_name == "article_list":
data = { data = {