diff --git a/Elwig/Models/WineCult.cs b/Elwig/Models/WineCult.cs
index 4c66c05..eeb1341 100644
--- a/Elwig/Models/WineCult.cs
+++ b/Elwig/Models/WineCult.cs
@@ -12,6 +12,6 @@ namespace Elwig.Models {
         public string Name { get; set; }
 
         [Column("description")]
-        public string Description { get; set; }
+        public string? Description { get; set; }
     }
 }
diff --git a/Elwig/Windows/AdministrationWindow.cs b/Elwig/Windows/AdministrationWindow.cs
index e0ad153..532b0d4 100644
--- a/Elwig/Windows/AdministrationWindow.cs
+++ b/Elwig/Windows/AdministrationWindow.cs
@@ -71,7 +71,7 @@ namespace Elwig.Windows {
             CheckBoxInputs = ControlUtils.FindAllChildren<CheckBox>(this, ExemptInputs).ToArray();
             CheckComboBoxInputs = ControlUtils.FindAllChildren<CheckComboBox>(this, ExemptInputs).ToArray();
             RadioButtonInputs = ControlUtils.FindAllChildren<RadioButton>(this, ExemptInputs).ToArray();
-            PlzInputs = TextBoxInputs.Where(tb => "PLZ".Equals(tb.Tag)).ToArray();
+            PlzInputs = ControlUtils.FindAllChildren<TextBox>(this).Where(tb => "PLZ".Equals(tb.Tag)).ToArray();
             PlzOrtInputs = PlzInputs.Select(tb => ControlUtils.FindNextSibling<ComboBox>(tb) ?? throw new MissingMemberException()).ToArray();
             foreach (var tb in TextBoxInputs)
                 Valid[tb] = true;
@@ -317,7 +317,7 @@ namespace Elwig.Windows {
             );
 
         protected void UpdatePlz(TextBox plzInput, ComboBox ortInput) {
-            var plzInputValid = GetInputValid(plzInput);
+            var plzInputValid = Validator.CheckPlz(plzInput, RequiredInputs.Contains(plzInput), Context).IsValid;
             var item = ortInput.SelectedItem;
             var list = plzInputValid && plzInput.Text.Length == 4 ? Context.Postleitzahlen.Find(int.Parse(plzInput.Text))?.Orte.ToList() : null;
             ControlUtils.RenewItemsSource(ortInput, list, i => (i as AT_PlzDest)?.Id);
@@ -551,5 +551,13 @@ namespace Elwig.Windows {
         protected void LfbisNrInput_LostFocus(object sender, RoutedEventArgs evt) {
             InputLostFocus((TextBox)sender, Validator.CheckLfbisNr);
         }
+
+        protected void UpperCaseInput_TextChanged(object sender, RoutedEventArgs evt) {
+            InputTextChanged((TextBox)sender, Validator.CheckUpperCase);
+        }
+
+        protected void UpperCaseInput_LostFocus(object sender, RoutedEventArgs evt) {
+            InputLostFocus((TextBox)sender, Validator.CheckUpperCase);
+        }
     }
 }
diff --git a/Elwig/Windows/BaseDataWindow.xaml b/Elwig/Windows/BaseDataWindow.xaml
index 690782e..bbafb13 100644
--- a/Elwig/Windows/BaseDataWindow.xaml
+++ b/Elwig/Windows/BaseDataWindow.xaml
@@ -133,13 +133,181 @@
                 </Grid>
             </TabItem>
             <TabItem Header="Zweigstellen">
+                <Grid>
+                    <Grid.ColumnDefinitions>
+                        <ColumnDefinition Width="260"/>
+                        <ColumnDefinition Width="*"/>
+                    </Grid.ColumnDefinitions>
+                    <ListBox x:Name="BranchList" Margin="10,10,35,10"
+                             SelectionChanged="BranchList_SelectionChanged" Grid.Column="0">
+                        <ListBox.ItemTemplate>
+                            <DataTemplate>
+                                <StackPanel Orientation="Horizontal">
+                                    <TextBlock Text="{Binding ZwstId}" Width="20"/>
+                                    <TextBlock Text="{Binding Name}"/>
+                                </StackPanel>
+                            </DataTemplate>
+                        </ListBox.ItemTemplate>
+                    </ListBox>
+                    <Button x:Name="BranchAddButton" Content="&#xF8AA;" FontFamily="Segoe MDL2 Assets" FontSize="11" Padding="0,1.5,0,0" IsEnabled="False"
+                            Click="BranchAddButton_Click"
+                            VerticalAlignment="Center" HorizontalAlignment="Right" Width="25" Height="25" Margin="5,0,5,30" Grid.Column="0"/>
+                    <Button x:Name="BranchDeleteButton" Content="&#xF8AB;" FontFamily="Segoe MDL2 Assets" FontSize="11" Padding="0,1.5,0,0" IsEnabled="False"
+                            Click="BranchDeleteButton_Click"
+                            VerticalAlignment="Center" HorizontalAlignment="Right" Width="25" Height="25" Margin="5,30,5,0" Grid.Column="0"/>
 
+                    <Grid Grid.Column="1" Margin="0,10,0,10">
+                        <Grid.ColumnDefinitions>
+                            <ColumnDefinition Width="110"/>
+                            <ColumnDefinition Width="*"/>
+                        </Grid.ColumnDefinitions>
+
+                        <Label Content="Identifikator:" Margin="10,10,0,10"/>
+                        <TextBox x:Name="BranchIdInput" Grid.Column="1" Margin="0,10,10,10" Width="50" HorizontalAlignment="Left"
+                                 TextChanged="Branch_Changed"/>
+
+                        <Label Content="Name:" Margin="10,40,0,10"/>
+                        <TextBox x:Name="BranchNameInput" Grid.Column="1" Margin="0,40,10,10" Width="150" HorizontalAlignment="Left"
+                                 TextChanged="Branch_Changed"/>
+
+                        <Label Content="PLZ/Ort:" Margin="10,70,0,10"/>
+                        <TextBox x:Name="BranchPlzInput" Margin="0,70,0,0" Width="42" Grid.Column="1" HorizontalAlignment="Left"
+                                 TextChanged="BranchPlzInput_TextChanged" LostFocus="PlzInput_LostFocus" Tag="PLZ"/>
+                        <ComboBox x:Name="BranchOrtInput" ItemTemplate="{StaticResource PostalDestTemplate}" TextSearch.TextPath="Ort.Name"
+                                  Margin="47,70,10,0" Grid.Column="1" Grid.ColumnSpan="3" Width="253" HorizontalAlignment="Left"
+                                  SelectionChanged="Branch_Changed"/>
+
+                        <Label Content="Adresse:" Margin="10,100,0,10"/>
+                        <TextBox x:Name="BranchAddressInput" Grid.Column="1" Margin="0,100,10,10" Width="300" HorizontalAlignment="Left"
+                                 TextChanged="Branch_Changed"/>
+
+                        <Label Content="Tel.-Nr. (Festnetz):" Margin="10,130,0,10"/>
+                        <TextBox x:Name="BranchPhoneNrInput" Grid.Column="1" Margin="0,130,10,10" Width="200" HorizontalAlignment="Left"
+                                 TextChanged="BranchPhoneNr_TextChanged"/>
+
+                        <Label Content="Fax-Nr.:" Margin="10,160,0,10"/>
+                        <TextBox x:Name="BranchFaxNrInput" Grid.Column="1" Margin="0,160,10,10" Width="200" HorizontalAlignment="Left"
+                                 TextChanged="BranchPhoneNr_TextChanged"/>
+
+                        <Label Content="Tel.-Nr. (mobil):" Margin="10,190,0,10"/>
+                        <TextBox x:Name="BranchMobileNrInput" Grid.Column="1" Margin="0,190,10,10" Width="200" HorizontalAlignment="Left"
+                                 TextChanged="BranchPhoneNr_TextChanged"/>
+                    </Grid>
+                </Grid>
             </TabItem>
             <TabItem Header="Sortenattribute">
+                <Grid>
+                    <Grid.ColumnDefinitions>
+                        <ColumnDefinition Width="260"/>
+                        <ColumnDefinition Width="*"/>
+                    </Grid.ColumnDefinitions>
+                    <ListBox x:Name="WineAttributeList" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10,10,35,10"
+                             SelectionChanged="WineAttributeList_SelectionChanged">
+                        <ListBox.ItemTemplate>
+                            <DataTemplate>
+                                <StackPanel Orientation="Horizontal">
+                                    <TextBlock Text="{Binding AttrId}" Width="30"/>
+                                    <TextBlock Text="{Binding Name}" Width="80"/>
+                                    <TextBlock Text="{Binding MaxKgPerHa, StringFormat='{}{0:N0} kg'}" Width="60" TextAlignment="Right" Margin="0,0,5,0"/>
+                                    <CheckBox IsChecked="{Binding IsActive}" IsEnabled="False" Margin="10,0,10,0"/>
+                                </StackPanel>
+                            </DataTemplate>
+                        </ListBox.ItemTemplate>
+                    </ListBox>
+                    <Button x:Name="WineAttributeAddButton" Content="&#xF8AA;" FontFamily="Segoe MDL2 Assets" FontSize="11" Padding="0,1.5,0,0" IsEnabled="False"
+                            Click="WineAttributeAddButton_Click"
+                            VerticalAlignment="Center" HorizontalAlignment="Right" Width="25" Height="25" Margin="5,0,5,30"/>
+                    <Button x:Name="WineAttributeDeleteButton" Content="&#xF8AB;" FontFamily="Segoe MDL2 Assets" FontSize="11" Padding="0,1.5,0,0" IsEnabled="False"
+                            Click="WineAttributeDeleteButton_Click"
+                            VerticalAlignment="Center" HorizontalAlignment="Right" Width="25" Height="25" Margin="5,30,5,0"/>
 
+                    <Grid Grid.Column="1" Margin="0,10,0,10">
+                        <Grid.ColumnDefinitions>
+                            <ColumnDefinition Width="100"/>
+                            <ColumnDefinition Width="*"/>
+                        </Grid.ColumnDefinitions>
+
+                        <Label Content="Identifikator:" Margin="10,10,0,10"/>
+                        <TextBox x:Name="WineAttributeIdInput" Grid.Column="1" Margin="0,10,10,10" Width="50" HorizontalAlignment="Left"
+                                 TextChanged="WineAttributeIdInput_TextChanged"/>
+
+                        <Label Content="Name:" Margin="10,40,0,10"/>
+                        <TextBox x:Name="WineAttributeNameInput" Grid.Column="1" Margin="0,40,10,10" Width="150" HorizontalAlignment="Left"
+                                 TextChanged="WineAttribute_Changed"/>
+
+                        <CheckBox x:Name="WineAttributeActiveInput" Content="In Übernahme-Fenster anzeigen"
+                                  Grid.Column="1" Margin="0,74,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"
+                                  Checked="WineAttribute_Changed" Unchecked="WineAttribute_Changed"/>
+
+                        <GroupBox Header="Auszahlung" Grid.ColumnSpan="2" Margin="10,100,10,10">
+                            <Grid>
+                                <Label Content="Max. Ertrag:" Margin="10,10,0,10"/>
+                                <Grid Grid.Column="1" Width="80" Height="25" Margin="84,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top">
+                                    <TextBox x:Name="WineAttributeMaxKgPerHaInput" TextAlignment="Right" Padding="2,2,30,2"
+                                             TextChanged="WineAttributeMaxKgPerHaInput_TextChanged"/>
+                                    <Label Content="kg/ha" Margin="0,4,3,0" HorizontalAlignment="Right" FontSize="10"/>
+                                </Grid>
+
+                                <CheckBox x:Name="WineAttributeStrictInput" Content="Strikte Trennung zu Flächenbindung ohne Attribut"
+                                          Margin="10,50,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"
+                                          Checked="WineAttributeStrictInput_Changed" Unchecked="WineAttributeStrictInput_Changed"/>
+
+                                <Label x:Name="WineAttributeFillLowerLabel" Content="Wenn möglich, Flächenbindung ohne Attribut..." Margin="10,75,10,10"/>
+                                <ComboBox x:Name="WineAttributeFillLowerInput" Width="300"
+                                          Margin="10,100,10,10" HorizontalAlignment="Left"
+                                          SelectionChanged="WineAttribute_Changed">
+                                    <ComboBoxItem>...nicht auffüllen</ComboBoxItem>
+                                    <ComboBoxItem>...bis Lieferpflicht auffüllen</ComboBoxItem>
+                                    <ComboBoxItem>...bis Lieferrecht auffüllen</ComboBoxItem>
+                                </ComboBox>
+                            </Grid>
+                        </GroupBox>
+                    </Grid>
+                </Grid>
             </TabItem>
             <TabItem Header="Bewirtschaftungsarten">
+                <Grid>
+                    <Grid.ColumnDefinitions>
+                        <ColumnDefinition Width="260"/>
+                        <ColumnDefinition Width="*"/>
+                    </Grid.ColumnDefinitions>
+                    <ListBox x:Name="WineCultivationList" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10,10,35,10"
+                             SelectionChanged="WineCultivationList_SelectionChanged">
+                        <ListBox.ItemTemplate>
+                            <DataTemplate>
+                                <StackPanel Orientation="Horizontal">
+                                    <TextBlock Text="{Binding CultId}" Width="30"/>
+                                    <TextBlock Text="{Binding Name}"/>
+                                </StackPanel>
+                            </DataTemplate>
+                        </ListBox.ItemTemplate>
+                    </ListBox>
+                    <Button x:Name="WineCultivationAddButton" Content="&#xF8AA;" FontFamily="Segoe MDL2 Assets" FontSize="11" Padding="0,1.5,0,0" IsEnabled="False"
+                            Click="WineCultivationAddButton_Click"
+                            VerticalAlignment="Center" HorizontalAlignment="Right" Width="25" Height="25" Margin="5,0,5,30"/>
+                    <Button x:Name="WineCultivationDeleteButton" Content="&#xF8AB;" FontFamily="Segoe MDL2 Assets" FontSize="11" Padding="0,1.5,0,0" IsEnabled="False"
+                            Click="WineCultivationDeleteButton_Click"
+                            VerticalAlignment="Center" HorizontalAlignment="Right" Width="25" Height="25" Margin="5,30,5,0"/>
 
+                    <Grid Grid.Column="1" Margin="0,10,0,10">
+                        <Grid.ColumnDefinitions>
+                            <ColumnDefinition Width="100"/>
+                            <ColumnDefinition Width="*"/>
+                        </Grid.ColumnDefinitions>
+
+                        <Label Content="Identifikator:" Margin="10,10,0,10"/>
+                        <TextBox x:Name="WineCultivationIdInput" Grid.Column="1" Margin="0,10,10,10" Width="50" HorizontalAlignment="Left"
+                                 TextChanged="WineCultivationIdInput_TextChanged"/>
+
+                        <Label Content="Name:" Margin="10,40,0,10"/>
+                        <TextBox x:Name="WineCultivationNameInput" Grid.Column="1" Margin="0,40,10,10" Width="150" HorizontalAlignment="Left"
+                                 TextChanged="WineCultivation_Changed"/>
+
+                        <Label Content="Beschreibung:" Margin="10,70,0,10"/>
+                        <TextBox x:Name="WineCultivationDescriptionInput" Grid.Column="1" Margin="0,70,10,10"
+                                 TextChanged="WineCultivation_Changed"/>
+                    </Grid>
+                </Grid>
             </TabItem>
             <TabItem Header="Flächenbindungsverträge">
 
@@ -198,7 +366,7 @@
 
                             <Label Content="Name:" Grid.Column="1" Margin="10,40,10,10"/>
                             <TextBox x:Name="SeasonModifierNameInput" Grid.Column="2" Margin="0,40,10,10"
-                                     TextChanged="SeasonModifierNameInput_TextChanged"/>
+                                     TextChanged="SeasonModifier_Changed"/>
 
                             <Label Content="Relativ:" Grid.Column="1" Margin="10,70,10,10"/>
                             <TextBox x:Name="SeasonModifierRelInput" Grid.Column="2" Margin="0,70,10,10"
diff --git a/Elwig/Windows/BaseDataWindow.xaml.Branch.cs b/Elwig/Windows/BaseDataWindow.xaml.Branch.cs
new file mode 100644
index 0000000..c230dfe
--- /dev/null
+++ b/Elwig/Windows/BaseDataWindow.xaml.Branch.cs
@@ -0,0 +1,150 @@
+using Elwig.Models;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Windows.Controls;
+using System.Windows;
+using Elwig.Helpers;
+using Microsoft.EntityFrameworkCore;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows.Data;
+
+namespace Elwig.Windows {
+    public partial class BaseDataWindow {
+
+        private Dictionary<string, string?>? _branches = null;
+        private Dictionary<Branch, string>? _branchIds = null;
+        private ObservableCollection<Branch>? _branchList = null;
+        private bool _branchChanged = false;
+        private bool _branchUpdate = false;
+
+        private void BranchesInitEditing() {
+            _branchList = new(Context.Branches.OrderBy(b => b.Name).ToList());
+            _branches = _branchList.ToDictionary(b => b.ZwstId, b => (string?)b.ZwstId);
+            _branchIds = _branchList.ToDictionary(b => b, b => b.ZwstId);
+            ControlUtils.RenewItemsSource(BranchList, _branchList, b => (b as Branch)?.ZwstId);
+        }
+
+        private void BranchesFinishEditing() {
+            ControlUtils.RenewItemsSource(BranchList, Context.Branches.OrderBy(b => b.Name).ToList(), b => (b as Branch)?.ZwstId);
+            _branchList = null;
+            _branches = null;
+            _branchIds = null;
+            _branchChanged = false;
+
+            BranchAddButton.IsEnabled = false;
+            BranchDeleteButton.IsEnabled = false;
+        }
+
+        private async Task BranchesSave() {
+            if (!_branchChanged || _branchList == null || _branches == null || _branchIds == null)
+                return;
+
+            foreach (var (zwstid, _) in _branches.Where(b => b.Value == null)) {
+                Context.Remove(Context.Branches.Find(zwstid));
+            }
+            foreach (var (branch, old) in _branchIds) {
+                branch.ZwstId = old;
+            }
+            foreach (var (old, zwstid) in _branches.Where(b => b.Value != null)) {
+                Context.Update(Context.Branches.Find(old));
+            }
+            await Context.SaveChangesAsync();
+
+            foreach (var (old, zwstid) in _branches.Where(b => b.Value != null)) {
+                await Context.Database.ExecuteSqlAsync($"UPDATE branch SET zwstid = {zwstid} WHERE zwstid = {old}");
+            }
+            await Context.SaveChangesAsync();
+
+            foreach (var branch in _branchList.Where(b => !_branchIds.ContainsKey(b))) {
+                if (branch.ZwstId == null) continue;
+                await Context.AddAsync(branch);
+            }
+            await Context.SaveChangesAsync();
+        }
+
+        private void BranchList_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
+            UpdateButtons();
+            _branchUpdate = true;
+            if (BranchList.SelectedItem is not Branch branch) {
+                BranchIdInput.Text = "";
+                BranchNameInput.Text = "";
+                BranchPlzInput.Text = "";
+                BranchAddressInput.Text = "";
+                BranchPhoneNrInput.Text = "";
+                BranchFaxNrInput.Text = "";
+                BranchMobileNrInput.Text = "";
+            } else {
+                BranchIdInput.Text = branch.ZwstId;
+                BranchNameInput.Text = branch.Name;
+                BranchPlzInput.Text = branch.PostalDest?.AtPlz?.Plz.ToString() ?? "";
+                ControlUtils.SelectComboBoxItem(BranchOrtInput, o => (o as AT_PlzDest)?.Okz, branch.PostalDest?.AtPlz?.Okz);
+                BranchAddressInput.Text = branch.Address;
+                BranchPhoneNrInput.Text = branch.PhoneNr;
+                BranchFaxNrInput.Text = branch.FaxNr;
+                BranchMobileNrInput.Text = branch.MobileNr;
+            }
+            _branchUpdate = false;
+        }
+
+        private void BranchAddButton_Click(object sender, RoutedEventArgs evt) {
+            if (_branchList == null) return;
+            _branchChanged = true;
+            var item = Context.CreateProxy<Branch>();
+            _branchList.Add(item);
+            BranchList.SelectedItem = item;
+            UpdateButtons();
+        }
+
+        private void BranchDeleteButton_Click(object sender, RoutedEventArgs evt) {
+            if (_branchList == null || _branches == null) return;
+            _branchChanged = true;
+            var idx = BranchList.SelectedIndex;
+            var item = _branchList[idx];
+            _branches[item.ZwstId] = null;
+            _branchList.RemoveAt(idx);
+            BranchList.SelectedIndex = idx < _branchList.Count ? idx : idx - 1;
+            UpdateButtons();
+        }
+
+        private void Branch_Changed(object? sender, RoutedEventArgs? evt) {
+            if (_branchUpdate || (!IsEditing && !IsCreating) || BranchList.SelectedItem is not Branch branch || _branches == null || _branchIds == null) return;
+            _branchChanged = _branchChanged ||
+                BranchIdInput.Text != branch.ZwstId ||
+                BranchNameInput.Text != branch.Name ||
+                BranchPlzInput.Text != (branch.PostalDest?.AtPlz?.Plz.ToString() ?? "") ||
+                (BranchOrtInput.SelectedItem as AT_PlzDest)?.Okz != branch.PostalDest?.AtPlz?.Okz ||
+                BranchAddressInput.Text != branch.Address ||
+                BranchPhoneNrInput.Text != (branch.PhoneNr ?? "") ||
+                BranchFaxNrInput.Text != (branch.FaxNr ?? "") ||
+                BranchMobileNrInput.Text != (branch.MobileNr ?? "");
+
+            var old = _branchIds.GetValueOrDefault(branch);
+            var id = BranchIdInput.Text;
+            if (old != null) _branches[old] = id;
+            branch.ZwstId = id;
+            branch.Name = BranchNameInput.Text;
+            branch.PostalDestId = (BranchOrtInput.SelectedItem as AT_PlzDest)?.Id;
+            branch.Address = BranchAddressInput.Text;
+            branch.PhoneNr = BranchPhoneNrInput.Text;
+            branch.FaxNr = BranchFaxNrInput.Text;
+            branch.MobileNr = BranchMobileNrInput.Text;
+            if (branch.PhoneNr.Length == 0) branch.PhoneNr = null;
+            if (branch.FaxNr.Length == 0) branch.FaxNr = null;
+            if (branch.MobileNr.Length == 0) branch.MobileNr = null;
+
+            CollectionViewSource.GetDefaultView(_branchList).Refresh();
+            UpdateButtons();
+        }
+
+        private void BranchPlzInput_TextChanged(object sender, TextChangedEventArgs evt) {
+            PlzInput_TextChanged(sender, evt);
+            Branch_Changed(sender, evt);
+        }
+
+        private void BranchPhoneNr_TextChanged(object sender, TextChangedEventArgs evt) {
+            PhoneNrInput_TextChanged(sender, evt);
+            Branch_Changed(sender, evt);
+        }
+    }
+}
diff --git a/Elwig/Windows/BaseDataWindow.xaml.Mod.cs b/Elwig/Windows/BaseDataWindow.xaml.Mod.cs
new file mode 100644
index 0000000..b610def
--- /dev/null
+++ b/Elwig/Windows/BaseDataWindow.xaml.Mod.cs
@@ -0,0 +1,170 @@
+using Elwig.Helpers;
+using Elwig.Models;
+using Microsoft.EntityFrameworkCore;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+
+namespace Elwig.Windows {
+    public partial class BaseDataWindow {
+
+        private Dictionary<string, string?>? _mods = null;
+        private Dictionary<Modifier, string>? _modIds = null;
+        private ObservableCollection<Modifier>? _modList = null;
+        private bool _modChanged = false;
+        private bool _modUpdate = false;
+
+        private void ModifiersInitEditing() {
+            var year = (SeasonList.SelectedItem as Season)?.Year;
+            _modList = new(Context.Modifiers.Where(m => m.Year == year).OrderBy(m => m.Ordering).ToList());
+            _mods = _modList.ToDictionary(m => m.ModId, m => (string?)m.ModId);
+            _modIds = _modList.ToDictionary(m => m, m => m.ModId);
+            ControlUtils.RenewItemsSource(SeasonModifierList, _modList, m => (m as Modifier)?.ModId);
+        }
+
+        private void ModifiersFinishEditing() {
+            var year = (SeasonList.SelectedItem as Season)?.Year;
+            ControlUtils.RenewItemsSource(SeasonModifierList, Context.Modifiers.Where(m => m.Year == year).OrderBy(m => m.Ordering).ToList(), m => (m as Modifier)?.ModId);
+            _modList = null;
+            _mods = null;
+            _modIds = null;
+            _modChanged = false;
+
+            SeasonModifierUpButton.IsEnabled = false;
+            SeasonModifierDownButton.IsEnabled = false;
+            SeasonModifierAddButton.IsEnabled = false;
+            SeasonModifierDeleteButton.IsEnabled = false;
+        }
+
+        private async Task ModifiersSave() {
+            if (!_modChanged || _modList == null || _mods == null || _modIds == null)
+                return;
+
+            int i = 0;
+            foreach (var mod in _modList) mod.Ordering = ++i;
+
+            var year = (SeasonList.SelectedItem as Season)?.Year;
+            foreach (var (modid, _) in _mods.Where(m => m.Value == null)) {
+                Context.Remove(Context.Modifiers.Find(new object?[] { year, modid }));
+            }
+            foreach (var (mod, old) in _modIds) {
+                mod.ModId = old;
+            }
+            foreach (var (old, modid) in _mods.Where(m => m.Value != null)) {
+                Context.Update(Context.Modifiers.Find(new object?[] { year, old }));
+            }
+            await Context.SaveChangesAsync();
+
+            foreach (var (old, modid) in _mods.Where(m => m.Value != null)) {
+                await Context.Database.ExecuteSqlAsync($"UPDATE modifier SET modid = {modid} WHERE (year, modid) = ({year}, {old})");
+            }
+            await Context.SaveChangesAsync();
+
+            foreach (var mod in _modList.Where(m => !_modIds.ContainsKey(m))) {
+                if (mod.ModId == null) continue;
+                await Context.AddAsync(mod);
+            }
+            await Context.SaveChangesAsync();
+        }
+
+        private void SeasonModifierUpButton_Click(object sender, RoutedEventArgs evt) {
+            if (_modList == null) return;
+            _modChanged = true;
+            var idx = SeasonModifierList.SelectedIndex;
+            var item = _modList[idx];
+            _modList.RemoveAt(idx);
+            idx--;
+            _modList.Insert(idx, item);
+            SeasonModifierList.SelectedIndex = idx;
+            UpdateButtons();
+        }
+
+        private void SeasonModifierDownButton_Click(object sender, RoutedEventArgs evt) {
+            if (_modList == null) return;
+            _modChanged = true;
+            var idx = SeasonModifierList.SelectedIndex;
+            var item = _modList[idx];
+            _modList.RemoveAt(idx);
+            idx++;
+            _modList.Insert(idx, item);
+            SeasonModifierList.SelectedIndex = idx;
+            UpdateButtons();
+        }
+
+        private void SeasonModifierAddButton_Click(object sender, RoutedEventArgs evt) {
+            if (_modList == null || SeasonList.SelectedItem is not Season s) return;
+            _modChanged = true;
+            var idx = (SeasonModifierList.SelectedIndex != -1) ? SeasonModifierList.SelectedIndex + 1 : _modList.Count;
+            var item = Context.CreateProxy<Modifier>();
+            item.Year = s.Year;
+            _modList.Insert(idx, item);
+            SeasonModifierList.SelectedIndex = idx;
+            UpdateButtons();
+        }
+
+        private void SeasonModifierDeleteButton_Click(object sender, RoutedEventArgs evt) {
+            if (_modList == null || _mods == null) return;
+            _modChanged = true;
+            var idx = SeasonModifierList.SelectedIndex;
+            var item = _modList[idx];
+            _mods[item.ModId] = null;
+            _modList.RemoveAt(idx);
+            SeasonModifierList.SelectedIndex = idx < _modList.Count ? idx : idx - 1;
+            UpdateButtons();
+        }
+
+        private void SeasonModifierList_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
+            UpdateButtons();
+            _modUpdate = true;
+            if (SeasonModifierList.SelectedItem is not Modifier mod) {
+                SeasonModifierIdInput.Text = "";
+                SeasonModifierNameInput.Text = "";
+                SeasonModifierRelInput.Text = "";
+                SeasonModifierAbsInput.Text = "";
+            } else {
+                SeasonModifierIdInput.Text = mod.ModId;
+                SeasonModifierNameInput.Text = mod.Name;
+                SeasonModifierRelInput.Text = (mod.Rel * 100)?.ToString();
+                SeasonModifierAbsInput.Text = mod.Abs?.ToString();
+            }
+            _modUpdate = false;
+        }
+
+        private void SeasonModifier_Changed(object? sender, RoutedEventArgs evt) {
+            if (_modUpdate || (!IsEditing && !IsCreating) || SeasonModifierList.SelectedItem is not Modifier mod || SeasonList.SelectedItem is not Season s || _mods == null || _modIds == null) return;
+            _modChanged = _modChanged ||
+                SeasonModifierIdInput.Text != mod.ModId;
+
+            var old = _modIds.GetValueOrDefault(mod);
+            var id = SeasonModifierIdInput.Text ?? "";
+            if (old != null) _mods[old] = id;
+            mod.ModId = id;
+            mod.Rel = decimal.TryParse(SeasonModifierRelInput.Text, out var vRel) ? vRel / 100 : null;
+            if (mod.Rel != null) SeasonModifierAbsInput.Text = "";
+            mod.AbsValue = decimal.TryParse(SeasonModifierAbsInput.Text, out var vAbs) ? Utils.DecToDb(vAbs, s.Precision) : null;
+            if (mod.AbsValue != null) SeasonModifierRelInput.Text = "";
+
+            CollectionViewSource.GetDefaultView(_modList).Refresh();
+            UpdateButtons();
+        }
+
+        private void SeasonModifierIdInput_TextChanged(object sender, TextChangedEventArgs evt) {
+            UpperCaseInput_TextChanged(sender, evt);
+            SeasonModifier_Changed(sender, evt);
+        }
+
+        private void SeasonModifierRelInput_TextChanged(object sender, TextChangedEventArgs evt) {
+            // DecimalInput_TextChanged(sender, evt); FIXME '-' is ignored
+            SeasonModifier_Changed(sender, evt);
+        }
+
+        private void SeasonModifierAbsInput_TextChanged(object sender, TextChangedEventArgs evt) {
+            // DecimalInput_TextChanged(sender, evt); FIXME '-' is ignored
+            SeasonModifier_Changed(sender, evt);
+        }
+    }
+}
diff --git a/Elwig/Windows/BaseDataWindow.xaml.WineAttr.cs b/Elwig/Windows/BaseDataWindow.xaml.WineAttr.cs
new file mode 100644
index 0000000..449881a
--- /dev/null
+++ b/Elwig/Windows/BaseDataWindow.xaml.WineAttr.cs
@@ -0,0 +1,150 @@
+using Elwig.Helpers;
+using Elwig.Models;
+using Microsoft.EntityFrameworkCore;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows;
+
+namespace Elwig.Windows {
+    public partial class BaseDataWindow {
+
+        private Dictionary<string, string?>? _attrs = null;
+        private Dictionary<WineAttr, string>? _attrIds = null;
+        private ObservableCollection<WineAttr>? _attrList = null;
+        private bool _attrChanged = false;
+        private bool _attrUpdate = false;
+
+        private void WineAttributesInitEditing() {
+            SeasonList.IsEnabled = false;
+            _attrList = new(Context.WineAttributes.OrderBy(a => a.Name).ToList());
+            _attrs = _attrList.ToDictionary(a => a.AttrId, a => (string?)a.AttrId);
+            _attrIds = _attrList.ToDictionary(a => a, a => a.AttrId);
+            ControlUtils.RenewItemsSource(WineAttributeList, _attrList, a => (a as WineAttr)?.AttrId);
+        }
+
+        private void WineAttributesFinishEditing() {
+            ControlUtils.RenewItemsSource(WineAttributeList, Context.WineAttributes.OrderBy(a => a.Name).ToList(), a => (a as WineAttr)?.AttrId);
+            _attrList = null;
+            _attrs = null;
+            _attrIds = null;
+            _attrChanged = false;
+
+            WineAttributeAddButton.IsEnabled = false;
+            WineAttributeDeleteButton.IsEnabled = false;
+            SeasonList.IsEnabled = true;
+        }
+
+        private async Task WineAttributesSave() {
+            if (!_attrChanged || _attrList == null || _attrs == null || _attrIds == null)
+                return;
+
+            foreach (var (attrid, _) in _attrs.Where(a => a.Value == null)) {
+                Context.Remove(Context.WineAttributes.Find(attrid));
+            }
+            foreach (var (attr, old) in _attrIds) {
+                attr.AttrId = old;
+            }
+            foreach (var (old, attrid) in _attrs.Where(a => a.Value != null)) {
+                Context.Update(Context.WineAttributes.Find(old));
+            }
+            await Context.SaveChangesAsync();
+
+            foreach (var (old, attrid) in _attrs.Where(a => a.Value != null)) {
+                await Context.Database.ExecuteSqlAsync($"UPDATE wine_attribute SET attrid = {attrid} WHERE attrid = {old}");
+            }
+            await Context.SaveChangesAsync();
+
+            foreach (var attr in _attrList.Where(a => !_attrIds.ContainsKey(a))) {
+                if (attr.AttrId == null) continue;
+                await Context.AddAsync(attr);
+            }
+            await Context.SaveChangesAsync();
+        }
+
+        private void WineAttributeList_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
+            UpdateButtons();
+            _attrUpdate = true;
+            if (WineAttributeList.SelectedItem is not WineAttr attr) {
+                WineAttributeIdInput.Text = "";
+                WineAttributeNameInput.Text = "";
+                WineAttributeActiveInput.IsChecked = false;
+                WineAttributeMaxKgPerHaInput.Text = "";
+                WineAttributeStrictInput.IsChecked = false;
+                WineAttributeFillLowerInput.SelectedItem = null;
+            } else {
+                WineAttributeIdInput.Text = attr.AttrId;
+                WineAttributeNameInput.Text = attr.Name;
+                WineAttributeActiveInput.IsChecked = attr.IsActive;
+                WineAttributeMaxKgPerHaInput.Text = attr.MaxKgPerHa?.ToString() ?? "";
+                WineAttributeStrictInput.IsChecked = attr.IsStrict;
+                WineAttributeFillLowerInput.SelectedIndex = attr.FillLower;
+            }
+            _attrUpdate = false;
+        }
+
+        private void WineAttributeAddButton_Click(object sender, RoutedEventArgs evt) {
+            if (_attrList == null) return;
+            _attrChanged = true;
+            var item = Context.CreateProxy<WineAttr>();
+            _attrList.Add(item);
+            WineAttributeList.SelectedItem = item;
+            UpdateButtons();
+        }
+
+        private void WineAttributeDeleteButton_Click(object sender, RoutedEventArgs evt) {
+            if (_attrList == null || _attrs == null) return;
+            _attrChanged = true;
+            var idx = WineAttributeList.SelectedIndex;
+            var item = _attrList[idx];
+            _attrs[item.AttrId] = null;
+            _attrList.RemoveAt(idx);
+            WineAttributeList.SelectedIndex = idx < _attrList.Count ? idx : idx - 1;
+            UpdateButtons();
+        }
+
+        private void WineAttribute_Changed(object? sender, RoutedEventArgs? evt) {
+            if (_attrUpdate || (!IsEditing && !IsCreating) || WineAttributeList.SelectedItem is not WineAttr attr || _attrs == null || _attrIds == null) return;
+            _attrChanged = _attrChanged ||
+                WineAttributeIdInput.Text != attr.AttrId ||
+                WineAttributeNameInput.Text != attr.Name ||
+                WineAttributeActiveInput.IsChecked != attr.IsActive ||
+                WineAttributeMaxKgPerHaInput.Text != attr.MaxKgPerHa?.ToString() ||
+                WineAttributeStrictInput.IsChecked != attr.IsStrict ||
+                WineAttributeFillLowerInput.SelectedIndex != attr.FillLower;
+
+            var old = _attrIds.GetValueOrDefault(attr);
+            var id = WineAttributeIdInput.Text;
+            if (old != null) _attrs[old] = id;
+            attr.AttrId = id;
+            attr.Name = WineAttributeNameInput.Text ?? "";
+            attr.IsActive = WineAttributeActiveInput.IsChecked ?? false;
+            attr.MaxKgPerHa = WineAttributeMaxKgPerHaInput.Text.Length > 0 ? int.Parse(WineAttributeMaxKgPerHaInput.Text) : null;
+            attr.IsStrict = WineAttributeStrictInput.IsChecked ?? false;
+            attr.FillLower = WineAttributeFillLowerInput.SelectedIndex;
+
+            CollectionViewSource.GetDefaultView(_attrList).Refresh();
+            CollectionViewSource.GetDefaultView(_attrList).Refresh();
+            UpdateButtons();
+        }
+
+        private void WineAttributeIdInput_TextChanged(object sender, TextChangedEventArgs evt) {
+            UpperCaseInput_TextChanged(sender, evt);
+            WineAttribute_Changed(sender, evt);
+        }
+
+        private void WineAttributeMaxKgPerHaInput_TextChanged(object sender, TextChangedEventArgs evt) {
+            InputTextChanged((TextBox)sender, Validator.CheckInteger((TextBox)sender, false, 5));
+            WineAttribute_Changed(sender, evt);
+        }
+
+        private void WineAttributeStrictInput_Changed(object sender, RoutedEventArgs evt) {
+            WineAttributeFillLowerInput.Visibility = WineAttributeStrictInput.IsChecked == true ? Visibility.Visible : Visibility.Hidden;
+            WineAttributeFillLowerLabel.Visibility = WineAttributeFillLowerInput.Visibility;
+            WineAttribute_Changed(sender, evt);
+        }
+    }
+}
diff --git a/Elwig/Windows/BaseDataWindow.xaml.WineCult.cs b/Elwig/Windows/BaseDataWindow.xaml.WineCult.cs
new file mode 100644
index 0000000..035b54b
--- /dev/null
+++ b/Elwig/Windows/BaseDataWindow.xaml.WineCult.cs
@@ -0,0 +1,125 @@
+using Elwig.Helpers;
+using Elwig.Models;
+using Microsoft.EntityFrameworkCore;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows;
+
+namespace Elwig.Windows {
+    public partial class BaseDataWindow {
+
+        private Dictionary<string, string?>? _cults = null;
+        private Dictionary<WineCult, string>? _cultIds = null;
+        private ObservableCollection<WineCult>? _cultList = null;
+        private bool _cultChanged = false;
+        private bool _cultUpdate = false;
+
+        private void WineCultivationsInitEditing() {
+            _cultList = new(Context.WineCultivations.OrderBy(c => c.Name).ToList());
+            _cults = _cultList.ToDictionary(c => c.CultId, c => (string?)c.CultId);
+            _cultIds = _cultList.ToDictionary(c => c, c => c.CultId);
+            ControlUtils.RenewItemsSource(WineCultivationList, _cultList, c => (c as WineCult)?.CultId);
+        }
+
+        private void WineCultivationsFinishEditing() {
+            ControlUtils.RenewItemsSource(WineCultivationList, Context.WineCultivations.OrderBy(c => c.Name).ToList(), c => (c as WineCult)?.CultId);
+            _cultList = null;
+            _cults = null;
+            _cultIds = null;
+            _cultChanged = false;
+
+            WineCultivationAddButton.IsEnabled = false;
+            WineCultivationDeleteButton.IsEnabled = false;
+        }
+
+        private async Task WineCultivationsSave() {
+            if (!_cultChanged || _cultList == null || _cults == null || _cultIds == null)
+                return;
+
+            foreach (var (cultid, _) in _cults.Where(c => c.Value == null)) {
+                Context.Remove(Context.WineCultivations.Find(cultid));
+            }
+            foreach (var (cult, old) in _cultIds) {
+                cult.CultId = old;
+            }
+            foreach (var (old, cultid) in _cults.Where(c => c.Value != null)) {
+                Context.Update(Context.WineCultivations.Find(old));
+            }
+            await Context.SaveChangesAsync();
+
+            foreach (var (old, cultid) in _cults.Where(c => c.Value != null)) {
+                await Context.Database.ExecuteSqlAsync($"UPDATE wine_cultivation SET cultid = {cultid} WHERE cultid = {old}");
+            }
+            await Context.SaveChangesAsync();
+
+            foreach (var cult in _cultList.Where(c => !_cultIds.ContainsKey(c))) {
+                if (cult.CultId == null) continue;
+                await Context.AddAsync(cult);
+            }
+            await Context.SaveChangesAsync();
+        }
+
+        private void WineCultivationList_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
+            UpdateButtons();
+            _cultUpdate = true;
+            if (WineCultivationList.SelectedItem is not WineCult cult) {
+                WineCultivationIdInput.Text = "";
+                WineCultivationNameInput.Text = "";
+                WineCultivationDescriptionInput.Text = "";
+            } else {
+                WineCultivationIdInput.Text = cult.CultId;
+                WineCultivationNameInput.Text = cult.Name;
+                WineCultivationDescriptionInput.Text = cult.Description;
+            }
+            _cultUpdate = false;
+        }
+
+        private void WineCultivationAddButton_Click(object sender, RoutedEventArgs evt) {
+            if (_cultList == null) return;
+            _cultChanged = true;
+            var item = Context.CreateProxy<WineCult>();
+            _cultList.Add(item);
+            WineCultivationList.SelectedItem = item;
+            UpdateButtons();
+        }
+
+        private void WineCultivationDeleteButton_Click(object sender, RoutedEventArgs evt) {
+            if (_cultList == null || _cults == null) return;
+            _cultChanged = true;
+            var idx = WineCultivationList.SelectedIndex;
+            var item = _cultList[idx];
+            _cults[item.CultId] = null;
+            _cultList.RemoveAt(idx);
+            WineCultivationList.SelectedIndex = idx < _cultList.Count ? idx : idx - 1;
+            UpdateButtons();
+        }
+
+        private void WineCultivation_Changed(object? sender, RoutedEventArgs? evt) {
+            if (_cultUpdate || (!IsEditing && !IsCreating) || WineCultivationList.SelectedItem is not WineCult cult || _cults == null || _cultIds == null) return;
+            _cultChanged = _cultChanged ||
+                WineCultivationIdInput.Text != cult.CultId ||
+                WineCultivationNameInput.Text != cult.Name ||
+                WineCultivationDescriptionInput.Text != (cult.Description ?? "");
+
+            var old = _cultIds.GetValueOrDefault(cult);
+            var id = WineCultivationIdInput.Text ?? "";
+            if (old != null) _cults[old] = id;
+            cult.CultId = id;
+            cult.Name = WineCultivationNameInput.Text ?? "";
+            cult.Description = WineCultivationDescriptionInput.Text ?? "";
+            if (cult.Description.Length == 0) cult.Description = null;
+
+            CollectionViewSource.GetDefaultView(_cultList).Refresh();
+            UpdateButtons();
+        }
+
+        private void WineCultivationIdInput_TextChanged(object sender, TextChangedEventArgs evt) {
+            UpperCaseInput_TextChanged(sender, evt);
+            WineCultivation_Changed(sender, evt);
+        }
+    }
+}
diff --git a/Elwig/Windows/BaseDataWindow.xaml.cs b/Elwig/Windows/BaseDataWindow.xaml.cs
index 898b3f6..1b25e6b 100644
--- a/Elwig/Windows/BaseDataWindow.xaml.cs
+++ b/Elwig/Windows/BaseDataWindow.xaml.cs
@@ -2,23 +2,14 @@ using Elwig.Helpers;
 using Elwig.Models;
 using Microsoft.EntityFrameworkCore;
 using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
 using System.Linq;
 using System.Threading.Tasks;
 using System.Windows;
 using System.Windows.Controls;
-using System.Windows.Data;
 
 namespace Elwig.Windows {
     public partial class BaseDataWindow : AdministrationWindow {
 
-        private Dictionary<string, string?>? Modifiers = null;
-        private Dictionary<Modifier, string>? ModifierIds = null;
-        private ObservableCollection<Modifier>? ModifierList = null;
-        private bool ModifiersChanged = false;
-        private bool ModifierUpdate = false;
-
         public BaseDataWindow() {
             InitializeComponent();
             RequiredInputs = new Control[] {
@@ -27,12 +18,40 @@ namespace Elwig.Windows {
             };
             ExemptInputs = new Control[] {
                 ClientNameFull,
+                BranchIdInput, BranchNameInput, BranchPlzInput, BranchOrtInput,
+                BranchAddressInput, BranchPhoneNrInput, BranchFaxNrInput, BranchMobileNrInput,
+                WineAttributeIdInput, WineAttributeNameInput, WineAttributeActiveInput,
+                WineAttributeMaxKgPerHaInput, WineAttributeStrictInput, WineAttributeFillLowerInput,
+                WineCultivationIdInput, WineCultivationNameInput, WineCultivationDescriptionInput,
                 SeasonModifierIdInput, SeasonModifierNameInput, SeasonModifierRelInput, SeasonModifierAbsInput,
             };
+            WineAttributeFillLowerInput.Visibility = Visibility.Hidden;
+            WineAttributeFillLowerLabel.Visibility = Visibility.Hidden;
         }
 
         new protected void LockInputs() {
             base.LockInputs();
+
+            BranchIdInput.IsReadOnly = true;
+            BranchNameInput.IsReadOnly = true;
+            BranchPlzInput.IsReadOnly = true;
+            BranchOrtInput.IsEnabled = false;
+            BranchAddressInput.IsReadOnly = true;
+            BranchPhoneNrInput.IsReadOnly = true;
+            BranchFaxNrInput.IsReadOnly = true;
+            BranchMobileNrInput.IsReadOnly = true;
+
+            WineAttributeIdInput.IsReadOnly = true;
+            WineAttributeNameInput.IsReadOnly = true;
+            WineAttributeActiveInput.IsEnabled = false;
+            WineAttributeMaxKgPerHaInput.IsReadOnly = true;
+            WineAttributeStrictInput.IsEnabled = false;
+            WineAttributeFillLowerInput.IsEnabled = false;
+
+            WineCultivationIdInput.IsReadOnly = true;
+            WineCultivationNameInput.IsReadOnly = true;
+            WineCultivationDescriptionInput.IsReadOnly = true;
+
             SeasonModifierIdInput.IsReadOnly = true;
             SeasonModifierNameInput.IsReadOnly = true;
             SeasonModifierRelInput.IsReadOnly = true;
@@ -41,6 +60,27 @@ namespace Elwig.Windows {
 
         new protected void UnlockInputs() {
             base.UnlockInputs();
+
+            BranchIdInput.IsReadOnly = false;
+            BranchNameInput.IsReadOnly = false;
+            BranchPlzInput.IsReadOnly = false;
+            BranchOrtInput.IsEnabled = true;
+            BranchAddressInput.IsReadOnly = false;
+            BranchPhoneNrInput.IsReadOnly = false;
+            BranchFaxNrInput.IsReadOnly = false;
+            BranchMobileNrInput.IsReadOnly = false;
+
+            WineAttributeIdInput.IsReadOnly = false;
+            WineAttributeNameInput.IsReadOnly = false;
+            WineAttributeActiveInput.IsEnabled = true;
+            WineAttributeMaxKgPerHaInput.IsReadOnly = false;
+            WineAttributeStrictInput.IsEnabled = true;
+            WineAttributeFillLowerInput.IsEnabled = true;
+
+            WineCultivationIdInput.IsReadOnly = false;
+            WineCultivationNameInput.IsReadOnly = false;
+            WineCultivationDescriptionInput.IsReadOnly = false;
+
             SeasonModifierIdInput.IsReadOnly = false;
             SeasonModifierNameInput.IsReadOnly = false;
             SeasonModifierRelInput.IsReadOnly = false;
@@ -54,80 +94,43 @@ namespace Elwig.Windows {
 
         protected override async Task OnRenewContext() {
             await base.OnRenewContext();
-            ControlUtils.RenewItemsSource(SeasonList, await Context.Seasons.OrderByDescending(s => s.Year).ToListAsync(), s => (s as Season)?.Year, null, ControlUtils.RenewSourceDefault.First);
             var year = (SeasonList.SelectedItem as Season)?.Year;
+            ControlUtils.RenewItemsSource(SeasonList, await Context.Seasons.OrderByDescending(s => s.Year).ToListAsync(), s => (s as Season)?.Year, null, ControlUtils.RenewSourceDefault.First);
+            ControlUtils.RenewItemsSource(BranchList, await Context.Branches.OrderBy(b => b.Name).ToListAsync(), b => (b as Branch)?.ZwstId);
+            ControlUtils.RenewItemsSource(WineAttributeList, await Context.WineAttributes.OrderBy(a => a.Name).ToListAsync(), a => (a as WineAttr)?.AttrId);
+            ControlUtils.RenewItemsSource(WineCultivationList, await Context.WineCultivations.OrderBy(c => c.Name).ToListAsync(), c=> (c as WineCult)?.CultId);
             ControlUtils.RenewItemsSource(SeasonModifierList, await Context.Modifiers.Where(m => m.Year == year).OrderBy(m => m.Ordering).ToListAsync(), m => (m as Modifier)?.ModId);
         }
 
         protected override void UpdateButtons() {
             if (!IsEditing && !IsCreating) return;
-            bool ch = ModifiersChanged || HasChanged, v = IsValid;
+            bool ch = _branchChanged || _attrChanged || _cultChanged || _modChanged || HasChanged,
+                v = IsValid;
             CancelButton.IsEnabled = true;
             ResetButton.IsEnabled = ch;
             SaveButton.IsEnabled = ch && v;
+
+            BranchAddButton.IsEnabled = true;
+            BranchDeleteButton.IsEnabled = BranchList.SelectedIndex != -1;
+            WineAttributeAddButton.IsEnabled = true;
+            WineAttributeDeleteButton.IsEnabled = WineAttributeList.SelectedIndex != -1;
+            WineCultivationAddButton.IsEnabled = true;
+            WineCultivationDeleteButton.IsEnabled = WineCultivationList.SelectedIndex != -1;
+
             SeasonModifierUpButton.IsEnabled = SeasonModifierList.SelectedIndex >= 1;
-            SeasonModifierDownButton.IsEnabled = SeasonModifierList.SelectedIndex != -1 && SeasonModifierList.SelectedIndex < (ModifierList?.Count - 1 ?? 0);
+            SeasonModifierDownButton.IsEnabled = SeasonModifierList.SelectedIndex != -1 && SeasonModifierList.SelectedIndex < (_modList?.Count - 1 ?? 0);
             SeasonModifierAddButton.IsEnabled = true;
             SeasonModifierDeleteButton.IsEnabled = SeasonModifierList.SelectedIndex != -1;
         }
 
-        private void ModifiersInitEditing() {
-            var year = (SeasonList.SelectedItem as Season)?.Year;
-            Context.ChangeTracker.Clear();
-            ModifierList = new(Context.Modifiers.Where(m => m.Year == year).OrderBy(m => m.Ordering).ToList());
-            Modifiers = ModifierList.ToDictionary(m => m.ModId, m => m.ModId);
-            ModifierIds = ModifierList.ToDictionary(m => m, m => m.ModId);
-            ControlUtils.RenewItemsSource(SeasonModifierList, ModifierList, m => (m as Modifier)?.ModId);
-        }
-
-        private void ModifiersFinishEditing() {
-            var year = (SeasonList.SelectedItem as Season)?.Year;
-            ControlUtils.RenewItemsSource(SeasonModifierList, Context.Modifiers.Where(m => m.Year == year).OrderBy(m => m.Ordering).ToList(), m => (m as Modifier)?.ModId);
-            ModifierList = null;
-            Modifiers = null;
-            ModifierIds = null;
-            ModifiersChanged = false;
-
-            SeasonModifierUpButton.IsEnabled = false;
-            SeasonModifierDownButton.IsEnabled = false;
-            SeasonModifierAddButton.IsEnabled = false;
-            SeasonModifierDeleteButton.IsEnabled = false;
-        }
-
-        private async Task ModifiersSave() {
-            if (!ModifiersChanged || ModifierList == null || Modifiers == null || ModifierIds == null) return;
-            int i = 0;
-            foreach (var mod in ModifierList) mod.Ordering = ++i;
-
-            var year = (SeasonList.SelectedItem as Season)?.Year;
-            foreach (var (modid, _) in Modifiers.Where(m => m.Value == null)) {
-                Context.Remove(Context.Modifiers.Find(new object?[] { year, modid }));
-            }
-            foreach (var (mod, old) in ModifierIds) {
-                mod.ModId = old;
-            }
-            foreach (var (old, modid) in Modifiers.Where(m => m.Value != null)) {
-                Context.Update(Context.Modifiers.Find(new object?[] { year, old }));
-            }
-            await Context.SaveChangesAsync();
-
-            foreach (var (old, modid) in Modifiers.Where(m => m.Value != null)) {
-                await Context.Database.ExecuteSqlAsync($"UPDATE modifier SET modid = {modid} WHERE (year, modid) = ({year}, {old})");
-            }
-            await Context.SaveChangesAsync();
-
-            foreach (var mod in ModifierList.Where(m => !ModifierIds.ContainsKey(m))) {
-                if (mod.ModId == null) continue;
-                await Context.AddAsync(mod);
-            }
-            await Context.SaveChangesAsync();
-        }
-
         private void EditButton_Click(object sender, RoutedEventArgs evt) {
             IsEditing = true;
             EditButton.Visibility = Visibility.Hidden;
             ResetButton.Visibility = Visibility.Visible;
 
+            BranchesInitEditing();
+            WineAttributesInitEditing();
+            WineCultivationsInitEditing();
             ModifiersInitEditing();
             UnlockInputs();
             UpdateButtons();
@@ -141,6 +144,11 @@ namespace Elwig.Windows {
             CancelButton.IsEnabled = false;
             SaveButton.IsEnabled = false;
             ResetButton.IsEnabled = false;
+
+            Context.ChangeTracker.Clear();
+            BranchesFinishEditing();
+            WineCultivationsFinishEditing();
+            WineAttributesFinishEditing();
             ModifiersFinishEditing();
 
             ClearInputStates();
@@ -149,8 +157,17 @@ namespace Elwig.Windows {
         }
 
         private void ResetButton_Click(object sender, RoutedEventArgs evt) {
-            ModifiersChanged = false;
+            _branchChanged = false;
+            _attrChanged = false;
+            _cultChanged = false;
+            _modChanged = false;
+            Context.ChangeTracker.Clear();
+
+            BranchesInitEditing();
+            WineAttributesInitEditing();
+            WineCultivationsInitEditing();
             ModifiersInitEditing();
+
             ClearInputStates();
             FillInputs(App.Client);
             UpdateButtons();
@@ -159,6 +176,9 @@ namespace Elwig.Windows {
         private async void SaveButton_Click(object sender, RoutedEventArgs evt) {
             try {
                 await UpdateClientParameters(App.Client);
+                await BranchesSave();
+                await WineAttributesSave();
+                await WineCultivationsSave();
                 await ModifiersSave();
             } catch (Exception exc) {
                 var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
@@ -173,6 +193,10 @@ namespace Elwig.Windows {
             CancelButton.IsEnabled = false;
             SaveButton.IsEnabled = false;
             ResetButton.IsEnabled = false;
+
+            BranchesFinishEditing();
+            WineAttributesFinishEditing();
+            WineCultivationsFinishEditing();
             ModifiersFinishEditing();
 
             ClearInputStates();
@@ -238,53 +262,6 @@ namespace Elwig.Windows {
             await p.UpdateValues();
         }
 
-        private void SeasonModifierUpButton_Click(object sender, RoutedEventArgs evt) {
-            if (ModifierList == null) return;
-            ModifiersChanged = true;
-            var idx = SeasonModifierList.SelectedIndex;
-            var item = ModifierList[idx];
-            ModifierList.RemoveAt(idx);
-            idx--;
-            ModifierList.Insert(idx, item);
-            SeasonModifierList.SelectedIndex = idx;
-            UpdateButtons();
-        }
-
-        private void SeasonModifierDownButton_Click(object sender, RoutedEventArgs evt) {
-            if (ModifierList == null) return;
-            ModifiersChanged = true;
-            var idx = SeasonModifierList.SelectedIndex;
-            var item = ModifierList[idx];
-            ModifierList.RemoveAt(idx);
-            idx++;
-            ModifierList.Insert(idx, item);
-            SeasonModifierList.SelectedIndex = idx;
-            UpdateButtons();
-        }
-
-        private void SeasonModifierAddButton_Click(object sender, RoutedEventArgs evt) {
-            if (ModifierList == null || SeasonList.SelectedItem is not Season s) return;
-            ModifiersChanged = true;
-            var idx = (SeasonModifierList.SelectedIndex != -1) ? SeasonModifierList.SelectedIndex + 1 : ModifierList.Count;
-            var item = Context.CreateProxy<Modifier>();
-            item.Year = s.Year;
-            ModifierList.Insert(idx, item);
-            SeasonModifierList.SelectedIndex = idx;
-            UpdateButtons();
-        }
-
-        private void SeasonModifierDeleteButton_Click(object sender, RoutedEventArgs evt) {
-            if (ModifierList == null || Modifiers == null) return;
-            ModifiersChanged = true;
-            var idx = SeasonModifierList.SelectedIndex;
-            var item = ModifierList[idx];
-            Modifiers[item.ModId] = null;
-            ModifierList.RemoveAt(idx);
-            SeasonModifierList.SelectedIndex = idx < ModifierList.Count ? idx : idx - 1;
-            UpdateButtons();
-        }
-
-
         private void ClientNames_TextChanged(object sender, TextChangedEventArgs evt) {
             var suffix = ClientNameSuffixInput.Text.Length > 0 ? ClientNameSuffixInput.Text : null;
             ClientNameFull.Text = $"{ClientNameInput.Text}{(suffix != null ? $", {suffix}," : "")} {ClientNameTypeInput.Text}";
@@ -295,66 +272,5 @@ namespace Elwig.Windows {
             var year = (SeasonList.SelectedItem as Season)?.Year;
             SeasonModifierList.ItemsSource = await Context.Modifiers.Where(m => m.Year == year).OrderBy(m => m.Ordering).ToListAsync();
         }
-
-        private void SeasonModifierList_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
-            UpdateButtons();
-            ModifierUpdate = true;
-            if (SeasonModifierList.SelectedItem is not Modifier mod) {
-                SeasonModifierIdInput.Text = "";
-                SeasonModifierNameInput.Text = "";
-                SeasonModifierRelInput.Text = "";
-                SeasonModifierAbsInput.Text = "";
-            } else {
-                SeasonModifierIdInput.Text = mod.ModId;
-                SeasonModifierNameInput.Text = mod.Name;
-                SeasonModifierRelInput.Text = (mod.Rel * 100)?.ToString();
-                SeasonModifierAbsInput.Text = mod.Abs?.ToString();
-            }
-            ModifierUpdate = false;
-        }
-
-        private void SeasonModifierIdInput_TextChanged(object sender, TextChangedEventArgs evt) {
-            if ((!IsEditing && !IsCreating) || SeasonModifierList.SelectedItem is not Modifier mod || Modifiers == null || ModifierIds == null) return;
-            ModifiersChanged = ModifiersChanged || (SeasonModifierIdInput.Text ?? "") != ((SeasonModifierList.SelectedItem as Modifier)?.ModId ?? "");
-            if (ModifierUpdate) return;
-            var old = ModifierIds.GetValueOrDefault(mod);
-            var id = SeasonModifierIdInput.Text ?? "";
-            if (old != null) Modifiers[old] = id;
-            mod.ModId = id;
-            CollectionViewSource.GetDefaultView(ModifierList).Refresh();
-            UpdateButtons();
-        }
-
-        private void SeasonModifierNameInput_TextChanged(object sender, TextChangedEventArgs evt) {
-            if ((!IsEditing && !IsCreating) || SeasonModifierList.SelectedItem is not Modifier mod) return;
-            ModifiersChanged = ModifiersChanged || (SeasonModifierNameInput.Text ?? "") != ((SeasonModifierList.SelectedItem as Modifier)?.Name ?? "");
-            if (ModifierUpdate) return;
-            mod.Name = SeasonModifierNameInput.Text ?? "";
-            CollectionViewSource.GetDefaultView(ModifierList).Refresh();
-            UpdateButtons();
-        }
-
-        private void SeasonModifierRelInput_TextChanged(object sender, TextChangedEventArgs evt) {
-            // DecimalInput_TextChanged(sender, evt); FIXME '-' is ignored
-            if ((!IsEditing && !IsCreating) || SeasonModifierList.SelectedItem is not Modifier mod) return;
-            ModifiersChanged = ModifiersChanged || (SeasonModifierRelInput.Text ?? "") != ((SeasonModifierList.SelectedItem as Modifier)?.Rel?.ToString() ?? "");
-            if (ModifierUpdate) return;
-            mod.Rel = decimal.TryParse(SeasonModifierRelInput.Text, out var v) ? v / 100 : null;
-            if (mod.Rel != null) SeasonModifierAbsInput.Text = "";
-            CollectionViewSource.GetDefaultView(ModifierList).Refresh();
-            UpdateButtons();
-        }
-
-        private void SeasonModifierAbsInput_TextChanged(object sender, TextChangedEventArgs evt) {
-            // DecimalInput_TextChanged(sender, evt); FIXME '-' is ignored
-            if ((!IsEditing && !IsCreating) || SeasonModifierList.SelectedItem is not Modifier mod || SeasonList.SelectedItem is not Season s) return;
-            ModifiersChanged = ModifiersChanged || (SeasonModifierAbsInput.Text ?? "") != ((SeasonModifierList.SelectedItem as Modifier)?.Abs?.ToString() ?? "");
-            if (ModifierUpdate) return;
-            // FIXME ValueStr does not work in ModifierList when modifier is newly created
-            mod.AbsValue = decimal.TryParse(SeasonModifierAbsInput.Text, out var v) ? Utils.DecToDb(v, s.Precision) : null;
-            if (mod.AbsValue != null) SeasonModifierRelInput.Text = "";
-            CollectionViewSource.GetDefaultView(ModifierList).Refresh();
-            UpdateButtons();
-        }
     }
 }