Good suggestion, voted.
I've previously implemented the percentage discount part of your suggestion on our site for AssociatedToProduct attributes and the code change required is minimal. There's already a decimal field available for the price adjustment which is used for non-AssociatedToProduct attributes so I just reused this field to hold the percentage adjustment. This field gets hidden in the admin GUI by javascript when the associated to product option is selected but the field still exists and gets saved to the database so it's just a case of commenting out the code that hides it in the admin view:
_CreateOrUpdateProductAttributeValue.cshtml (changes in bold)
function toggleProductType() {
var selectedProductTypeId = $("#@Html.FieldIdFor(model => model.AttributeValueTypeId)").val();
if (selectedProductTypeId == @(((int)AttributeValueType.Simple).ToString())) {
$('#group-associated-product').hide();
$('#group-quantity').hide();
$('#group-price-adjustment').show();
$('#group-weight-adjustment').show();
$('#group-cost').show();
} else if (selectedProductTypeId == @(((int)AttributeValueType.AssociatedToProduct).ToString())) {
$('#group-associated-product').show();
$('#group-quantity').show();
//$('#group-price-adjustment').hide(); //Support percentage discounts on AssociatedToProduct product attributes
$('#group-weight-adjustment').hide();
$('#group-cost').hide();
}
}
and then using the value in the GetProductAttributeValuePriceAdjustment funtion of the price calculation service:
PriceCalculationService.cs (addition in bold)
/// <summary>
/// Get a price adjustment of a product attribute value
/// </summary>
/// <param name="value">Product attribute value</param>
/// <returns>Price adjustment</returns>
public virtual decimal GetProductAttributeValuePriceAdjustment(ProductAttributeValue value)
{
if (value == null)
throw new ArgumentNullException("value");
var adjustment = decimal.Zero;
switch (value.AttributeValueType)
{
case AttributeValueType.Simple:
{
//simple attribute
adjustment = value.PriceAdjustment;
}
break;
case AttributeValueType.AssociatedToProduct:
{
//bundled product
var associatedProduct = _productService.GetProductById(value.AssociatedProductId);
if (associatedProduct != null)
{
adjustment = GetFinalPrice(associatedProduct, _workContext.CurrentCustomer, includeDiscounts: true) * value.Quantity;
//Allow percentage based price adjustments on AssociatedToProduct product attributes!
adjustment = adjustment + (adjustment * value.PriceAdjustment / 100);
//rounding
if (_shoppingCartSettings.RoundPricesDuringCalculation)
adjustment = RoundingHelper.RoundPrice(adjustment);
//---------------------------------------------------------------//
}
}
break;
default:
break;
}
return adjustment;
}
Just these 2 changes allow the functionality you mention in point 1 above, and as you say it allows positive or negative adjustments and is calculated after tier prices are applied so supports some advanced pricing and discounting scenarios like "buy X get Y half price" which aren't possible with the standard discount implementation.
To support point 2 above I think an extra check box would be required to specify whether the discount is a fixed value or a percentage similar to how it currently works for discounts.