#18 - membership

This commit was merged in pull request #23.
This commit is contained in:
Martin Slachta
2026-06-17 11:15:09 +02:00
parent df5f9b1df4
commit c754e18a82
25 changed files with 885 additions and 35 deletions
@@ -1,6 +1,6 @@
<?php
/** Computes a form's total price as the sum of its elements' prices. */
/** Computes a form's total price as the sum of its elements' prices, reduced by membership discount. */
class RsvFormPriceCalculator {
public function calculate(RsvFormDefinition $definition, RsvFormData $data): float {
global $rsv_form_price_registry;
@@ -16,6 +16,7 @@ class RsvFormPriceCalculator {
$total += (float) $calculator($element, $data->getValue($element->getName()));
}
return $total;
$pct = (new RsvMembershipService())->discount_for($definition, $data);
return $total * (1.0 - max(0.0, min(100.0, $pct)) / 100.0);
}
}
@@ -4,8 +4,25 @@
final class RsvFormCalculatedValues {
/** @return array<string, mixed> */
public function for(RsvFormDefinition $definition, RsvFormData $data): array {
$calculator = new RsvFormPriceCalculator();
global $rsv_form_price_registry;
$price_before_discount = 0.0;
foreach ($definition->getElements() as $element) {
$element_calculator = $rsv_form_price_registry->get($element->getType());
if ($element_calculator === null) {
continue;
}
$price_before_discount += (float) $element_calculator($element, $data->getValue($element->getName()));
}
$discount_pct = (new RsvMembershipService())->discount_for($definition, $data);
$final_price = $calculator->calculate($definition, $data);
return [
'price' => (new RsvFormPriceCalculator())->calculate($definition, $data),
'price' => $final_price,
'price_before_discount' => $price_before_discount,
'discount_percent' => $discount_pct,
];
}
@@ -14,6 +31,6 @@ final class RsvFormCalculatedValues {
* @return list<string>
*/
public static function names(): array {
return ['price'];
return ['price', 'price_before_discount', 'discount_percent'];
}
}
+13 -1
View File
@@ -7,10 +7,12 @@ class RsvFormDefinition {
public string $email_key = "";
public array $membership = [];
public string $success_message = "";
/**
* @param array<int,mixed> $definition Full definition array including 'elements', 'email_key' and 'success_message'.
* @param array<string,mixed> $definition Full definition array including 'elements', 'email_key' and 'success_message'.
*/
public function __construct(string $id, array $definition) {
$this->_elements = [];
@@ -23,6 +25,7 @@ class RsvFormDefinition {
$this->_id = $id;
$this->email_key = $definition['email_key'] ?? '';
$this->membership = $definition['membership'] ?? [];
$this->success_message = $definition['success_message'] ?? '';
}
@@ -34,6 +37,15 @@ class RsvFormDefinition {
return $this->email_key;
}
/** @return array<int,array{program_id:int,discount:float,field:string}> */
public function getMembershipBindings(): array {
return $this->membership['bindings'] ?? [];
}
public function getMembershipCombine(): string {
return $this->membership['combine'] ?? 'max';
}
/** Template shown to the visitor after a successful submission. */
public function getSuccessMessage(): string {
return $this->success_message;
@@ -34,6 +34,9 @@ final class RsvFormDefinitionValidator {
$errors[] = 'Form must contain a submit button.';
}
// Validate membership bindings if present.
$errors = array_merge($errors, $this->validate_membership_bindings($definition));
return $errors;
}
@@ -109,4 +112,41 @@ final class RsvFormDefinitionValidator {
global $rsv_template_registry;
return new RsvTemplateEngine(registry: $rsv_template_registry);
}
/**
* @param array<string,mixed> $definition
* @return list<string>
*/
private function validate_membership_bindings(array $definition): array {
$errors = [];
$membership = $definition['membership'] ?? [];
if (!is_array($membership)) {
return $errors;
}
$bindings = $membership['bindings'] ?? [];
if (!is_array($bindings)) {
return $errors;
}
foreach ($bindings as $idx => $binding) {
if (!is_array($binding)) {
continue;
}
$program_id = intval($binding['program_id'] ?? 0);
$discount = floatval($binding['discount'] ?? 0.0);
if ($program_id <= 0) {
$errors[] = "Membership binding {$idx}: program_id must be a positive integer.";
}
if ($discount < 0.0 || $discount > 100.0) {
$errors[] = "Membership binding {$idx}: discount must be between 0 and 100.";
}
}
return $errors;
}
}