The DiscountTypeForm contract
component()— the Vue component id that renders the type’s configuration fields, resolved the same way a widget or slot component is.targetBuckets()— which of the four targeting buckets (limitation,exclusion,condition,reward) this type reads, driving which “Applies to” blocks the edit page shows. A type that discounts the whole cart or an entire shipping method rather than individual lines returns an empty array —ShippingDiscount, for example, targets nothing.toForm(array $data)— decodes the discount’s storeddatapayload for editing (minor currency units to decimals, and so on).toStorage(array $data)— the inverse: encodes the edited form values back into the shape stored indata.rules()— validation rules for the type’s owndata.*fields, keyed without thedata.prefix.summary(array $data, ?Currency $currency)— a one-line description of the discount’s effect for the discounts list ("15% off","Buy 2, get 1"). Returnnullwhen the type cannot summarise itself fromdataalone; the list falls back to the type’s own name.
Currency::decimal_places (via PriceCalculator), never a hardcoded factor — the same rule that applies to every price-handling code path in Lunar.
Registering a form
Map the discount type class to its form class from the owningSection:
PercentageOffForm, shows the shape for a type with no money scaling to do:
The JSON-editor fallback
A discount type with no registered form is not hidden — it falls back toLunar\Panel\Support\DiscountTypeForms\RawDataForm, a plain JSON editor over the type’s stored data, with every target bucket shown. This is what keeps a panel-unaware type (one written only against the Filament admin’s LunarPanelDiscountInterface, for example) editable in the panel instead of disappearing from it. RawDataForm::summary() always returns null, since the panel has no idea what an arbitrary payload means.
A discount type can implement both the Filament
Lunar\Admin\Base\LunarPanelDiscountInterface and the panel’s DiscountTypeForm to serve both admins from one class. Neither is required by the other.Discounts::addType(), AbstractDiscountType, and apply() — see Extending Discounts.