While this may not be the best solution, here is a working example for anyone else who has a similar problem (pure twig).
The saleContainsOtherProduct
variable is an indication that the sale did NOT match the criteria of only related to product type so in order to check for only related, use not saleContainsOtherProduct
.
Additionally, this only lists sales, but the code within the loop will work for discounts too, so just repeat with a for loop on craft.commerce.discounts.getAllDiscounts()
{% set ignoredProductTypes = ['bundles'] %}
{% for sale in craft.commerce.sales.getAllSales() %}
{% set saleContainsOtherProduct = false %}
{% set purchasables = [] %}
{% for id in sale.purchasableIds %}
{% set purchasable = craft.commerce.purchasables.getPurchasableById(id) %}
{% if purchasable.product.enabled %}
{% set purchasables = purchasables | merge([purchasable]) %}
{% endif %}
{% endfor %}
{% for id in sale.categoryIds %}
{% set category = craft.categories.id(id).one() %}
{% for product in craft.products().relatedTo(category).all() %}
{% if product.enabled %}
{% for purchasable in product.variants %}
{% set purchasables = purchasables | merge([purchasable]) %}
{% endfor %}
{% endif %}
{% endfor %}
{% endfor %}
{% for purchasable in purchasables if purchasable.product.type.handle not in ignoredProductTypes %}
{% set saleContainsOtherProduct = true %}
{% endfor %}
{% endfor %}