diff --git a/djangorota/rota/forms.py b/djangorota/rota/forms.py index 4860b4e..452a9fd 100644 --- a/djangorota/rota/forms.py +++ b/djangorota/rota/forms.py @@ -101,7 +101,9 @@ class RotaScheduleForm(forms.ModelForm): # Expose a small set of RotaBuilder constructor arguments on the form # so they can be set per-rota. These are stored inside `instance.options` - # and will be passed through by `RotaSchedule.to_rota_builder()`. + # and will be passed through by `RotaSchedule.to_rota_builder()` as + # legacy builder args (the remaining keys in `options` are treated as + # constraint configuration for `RotaConstraintOptions`). builder_args = { "balance_offset_modifier": {"type": "int", "default": 1, "help": "Balance offset modifier used by the builder"}, "ltft_balance_offset": {"type": "int", "default": 1, "help": "LTFT balance offset used by the builder"}, diff --git a/djangorota/rota/models.py b/djangorota/rota/models.py index 1f274d6..ba22f3f 100644 --- a/djangorota/rota/models.py +++ b/djangorota/rota/models.py @@ -57,10 +57,38 @@ class RotaSchedule(models.Model): delta = self.end_date - self.start_date weeks_to_rota = max(1, int(delta.days // 7)) - # Allow passing options directly if they match RotaBuilder kwargs - rb_kwargs = dict(self.options or {}) + # Build kwargs for RotaBuilder from any explicit builder args stored in + # `self.options` (kept for backward compatibility), and treat the + # remaining keys as constraint options which should map to + # `RotaConstraintOptions`. + opts = dict(self.options or {}) + builder_keys = [ + "balance_offset_modifier", + "ltft_balance_offset", + "use_previous_shifts", + "use_shift_balance_extra", + "use_bank_holiday_extra", + "allow_force_assignment_with_leave_conflict", + ] + rb_kwargs = {k: opts.pop(k) for k in builder_keys if k in opts} rb = RotaBuilder(start_date=self.start_date, weeks_to_rota=weeks_to_rota, name=self.name, **rb_kwargs) + # If the rota package exposes a typed RotaConstraintOptions model, use it + # to validate/coerce the remaining `opts` and attach it to the builder. + try: + RotaConstraintOptions = getattr(mod, "RotaConstraintOptions") + if opts: + # validate raw JSON into the typed model + opts_model = RotaConstraintOptions.model_validate(opts) + else: + opts_model = RotaConstraintOptions() + # attach the typed model to the builder (overrides defaults) + setattr(rb, "constraint_options_model", opts_model) + except Exception: + # If the model isn't available or validation fails, keep builder + # defaults and proceed (backwards compatible behaviour). + pass + # Add shifts (expecting list of dicts compatible with SingleShift) shifts_objs = [] for s in self.shifts or []: