0

Termine automatisch in Kalender generieren

Hallo, 

ich versuche schon jahrelang automatisierte Kalendereinträge zu erstellen, scheitere aber mangels Fachwissen. Das "Baukastenprinzip" führt mich erst recht nicht weiter. Chat GPT wirft mir auch Unbrauchbares aus...

Der Kalender sollte wie folgt generiert werden:

Jeder Schüler hat einen Vertrag (Verknüpfung) aus dessen Datensatz sich Daten für den Kalender entnehmen lassen:

- Beginn des Vertrages

- Wochentag

- Uhrzeit

- Dauer der Unterrichtsstunde

- Schüler aktiv/inaktiv

- Ende des Vertrages 

Bedingungen: 

- Der Kalender soll wöchentlich Termine ein Jahr im Voraus oder bis Ende des Vertrags erstellen und täglich aktualisieren.

- Termine älter als 2 Jahre sollen gelöscht werden.

- Termine sollen nur erstellt werden, wenn der Schüler als "aktiv" markiert ist.

Ich hoffe auf Euer wohlwollendes Fachwissen!

Manni

19 Antworten

null
    • Manfred_Surges
    • vor 1 Jahr
    • Gemeldet - anzeigen

    Die KI hat mir folgenden Code ausgegeben, leider sind in der ersten Zeile Fehlermeldungen: rot unterstrichen sind: =base. ,der Punkt hinter ("Verträge") und das Semikolon:

    let contracts = base.tables("Verträge").selectRecords(); // Lesen Sie alle Vertragsdatensätze
    let calendar = base.tables("Kalender"); // Referenz auf den Kalender-Datensatz

    for (let i = 0; i < contracts.length; i++) {
      let contract = contracts[i];
      let startDate = contract.field("Beginn des Vertrages");
      let endDate = contract.field("Ende des Vertrags");
      let dayOfWeek = contract.field("Wochentag");
      let time = contract.field("Uhrzeit");
      let duration = contract.field("Dauer der Unterrichtsstunde");
      let isActive = contract.field("Schüler aktiv/inaktiv");

      if (!isActive) {
        continue;
      }

      // Berechne das Enddatum der aktuellen Woche
      let currentWeekEnd = new Date();
      currentWeekEnd.setDate(currentWeekEnd.getDate() + (7 - currentWeekEnd.getDay()));

    // Berechne das Enddatum des nächsten Jahres
      let nextYearEnd = new Date();
      nextYearEnd.setFullYear(nextYearEnd.getFullYear() + 1);

    // Verwende das frühere Datum als Enddatum
      let targetEndDate = (endDate && endDate < nextYearEnd) ? endDate : nextYearEnd;

      // Berechne das Startdatum der nächsten Woche
      let nextWeekStart = new Date(currentWeekEnd.getTime());
      nextWeekStart.setDate(nextWeekStart.getDate() + 1);

    // Verwende das spätere Datum als Startdatum
      let targetStartDate = (startDate > nextWeekStart) ? startDate : nextWeekStart;

      while (targetStartDate <= targetEndDate) {
        // Überprüfen Sie, ob der aktuelle Tag dem Wochentag entspricht
        if (targetStartDate.getDay() === dayOfWeek) {
          let existingEvent = calendar.findFirst({
            "Schüler": contract.field("Schüler"),
            "Datum": targetStartDate,
            "Uhrzeit": time
          });

          if (existingEvent) {
            existingEvent.update({
              "Dauer": duration,
              "Schüler": contract.field("Schüler"),
              "Vertrag": contract.id
            });
          } else {
            calendar.createRecord({
              "Datum": targetStartDate,
              "Uhrzeit": time,
              "Dauer": duration,
              "Schüler": contract.field("Schüler"),
              "Vertrag": contract.id
            });
          }
        }

        // Fügen Sie eine Woche zum Zielstartdatum hinzu
        targetStartDate.setDate(targetStartDate.getDate() + 7);
      }
    }

    • Developer by Smartplanung
    • smartplanung
    • vor 1 Jahr
    • Gemeldet - anzeigen

    Hallo Manfred,

    schau Dir einmal die Datenbank an, welche ich im Anhang beigefügt habe. Im Dashboard kannst Du die Termine über den "Sync" Button aktualisieren. Sollen die Termine automatisch synchronisiert werden, aktiviere die Auswahl "Automatische Synchronisation".

    In der Tabelle "Schüler" trägst Du deine Schüler ein und in der Tabelle "Verträge", deine Verträge. Am einfachsten sollte es sein, die Verträge über einen Datensatz eines Schülers zu erstellen / zu verknüpfen.

    Folgendes solltest Du beachten:

    Zeitbasierte Automatisierungen gehen ohne fremde Anbindung nicht so einfach. Was jedoch funktioniert, ist ein Sync, sobald die Datenbank geöffnet wird. Das sollte ja passen. Möchte man die Termine sehen, muss man die Datenbank dafür öffnen ;-)

    Wenn Du dann einen neuen Vertrag anlegst oder das Vertragsende anpasst, kannst Du den Sync-Button nutzen, um fehlende Termine nachzutragen. .....mir fällt gerade ein, den Fall, wenn ein Vertrag früher beendet wird habe ich nicht berücksichtigt. Derzeit müsste man die Termine, welche plötzlich über das Vertragsende hinaus gehen händisch in der Tabelle "Termine" löschen. Auch wenn ein Schüler seinen Aktiv-Status verliert, wird das derzeit nicht berücksichtigt...Kann ich aber die Tage gerne noch ändern.

    Dann gäbe es noch das Problem der Zeitumstellung. In dem Script sind for-Schleifen drin. Diese verlangsamen das Script um einiges, weshalb ich hier die Funktion "do as server" aufrufe. Hierbei ist dann aber das Problem, dass alles auf dem Server durchgeführt wird. Die Sprache ist dort Englisch und die Uhrzeit ist dort um 1 Stunde verschoben. Dafür habe ich im Hintergrund zwei Felder angelegt, welche einmal die lokale Zeit (deines PCs/Browsers) und die Zeit des Servers festhält. Gibt es einen Zeitunterschied, werden die Zeiten entsprechend angepasst. Bitte halte da ein Auge drauf, ob das im Sommer noch richtig funktioniert.

    Sonst hoffe ich erstmal, dass ich dein Problem richtig verstanden habe und die Datenbank das tut, was sie soll. Ansonsten kannst Du dich gerne nochmal melden.

    Gruß
    Patrick

    • Developer by Smartplanung
    • smartplanung
    • vor 1 Jahr
    • Gemeldet - anzeigen

    Noch als Hinweis zu Chat GPT - was das Tool dir da ausgespuckt hat, ist JavaScript. Ninox basiert zwar auf JavaScript, jedoch ist die NX-Scriptsprache für die Endnutzer abgewandelt. JavaScript funktioniert für uns daher nur bedingt mit etwas komplizierten Methoden.

    • Manfred_Surges
    • vor 1 Jahr
    • Gemeldet - anzeigen

    Danke, werde es ausprobieren!

    • Developer by Smartplanung
    • smartplanung
    • vor 1 Jahr
    • Gemeldet - anzeigen

    Ich habe die Datenbank nochmal angepasst.

    • Wenn das Vertragsende geändert wurde, werden die Termine, welche darüber hinaus gehen gelöscht.
    • Wenn ein Schüler inaktiv wird, werden dessen Termine gelöscht.
    • Dafür dann entsprechend den Sync Button drücken oder die Datenbank neu öffnen, wenn automatischer Sync aktiv ist.

    Zudem habe ich die Verknüpfung zwischen Verträge und Schüler getauscht. Ein Schüler kann jetzt mehrere Verträge haben, ein Vertrag kann nur einen Schüler haben.

    Damit der Code nur einmal vorhanden ist, habe ich diesen in die globalen Funktionen gepackt. Dieser wird dann über das Öffnen der Datenbank oder durch drücken des Sync-Buttons ausgeführt. Das macht spätere Änderungen einfacher.

    • Manfred_Surges
    • vor 1 Jahr
    • Gemeldet - anzeigen

    Hallo Patrick, da funktioniert bei mir leider nicht. Der Termin des Schülers wird nur blau im Kalender angezeigt, ohne Uhrzeit. Würde es Sinn machen, wenn ich meine Datenbank poste? Die ist allerdings ziemlich groß...

    • Manfred_Surges
    • vor 1 Jahr
    • Gemeldet - anzeigen

    Der Dumme sitzt immer vor dem Computer... jetzt geht das. Wo finde ich den die globalen Funktionen? Du musst bedenken, ich bim Musiker und kann nur drag&drop...:-)

    • Manfred_Surges
    • vor 1 Jahr
    • Gemeldet - anzeigen

    Hallo nochmal Patrick, ich glaube so wird das nichts. Ich weiß nicht wo ich Änderungen in meiner vorhandenen Datenbank einsetzten soll. Daher schicke ich mal eine abgespeckte Version von meinen Daten. Wenn das machbar ist, könntest Du mir vielleicht mitteilen, wo ich in der Original- Datenbank Änderungen vornehmen muss. Oder Du hast eine andere Idee...

    • Developer by Smartplanung
    • smartplanung
    • vor 1 Jahr
    • Gemeldet - anzeigen

    Hallo Manfred,

    versuchen wir es mal:

    • Tabelle Dashboard erstellen
      • Button Sync erstellen
      • Ja/Nein Feld Automatische Synchronisation erstellen
      • Uhrzeit-Feld localTime erstellen
      • Uhrzeit-Feld serverTime erstellen
    • Tabelle Termine erstellen
      • Terminfeld Termin erstellen
      • Verknüpfung ==> auf die Untertabelle Vertrag
    • Anpassung Untertabelle Vertrag
      • Neues Auswahlfeld (nicht Mehrfachauswahlfeld!) Unterrichtsdauer2 einfügen und die Parameter 0:30, 0:45, 1:00 hinzufügen
      • Neues Zeitdauer Feld einfügen (Name bleibt gleich)
        • Bei dem Feld das Zahlenformat auf 61:16 stellen
      • Nun gehst Du nochmal auf das neue Feld Unterrichtsdauer2 und fügst bei Trigger nach Änderung folgenden Code ein:
      • switch Unterrichtsdauer2 do
        case 1:
            Zeitdauer := 30 * 60 * 1000
        case 2:
            Zeitdauer := 45 * 60 * 1000
        case 3:
            Zeitdauer := 60 * 60 * 1000
        end

        Ich arbeite nicht viel mit dem Kalender und mit dem Feld Zeitdauer. Das war in deinem Szenario das einfachste, die Dauer des Termins anzugeben. Wenn jemand von den anderen Kollegen hier im Forum weiß wie man die Dauer direkt über ein Auswahlfeld in den Termin bringen kann, hoffe ich auf Rückmeldung :-)

      • Das Feld Unterrichtsdauer entfernen, wenn Du alle Unterrichtszeiten der Verträge auf das neue Feld übertragen hast (das ist ein Mehrfachauswahlfeld)
    • Jetzt gehst Du nochmal zurück zur Tabelle Termine und fügst im Feld Termin unter Anzeigen als folgenden Code ein:
    • 'Vertrag'.'Schüler'.Vorname + " " + 'Vertrag'.'Schüler'.Nachname + " Dauer: " + 'Vertrag'.Zeitdauer + " Vertragsbeginn: " + 'Vertrag'.Beginn + " Vertragsende: " + 'Vertrag'.Ende

    Nun fügst Du die globale Funktion ein:

    Klicke auf Musik Zone2 (oben Links - damit kommst Du in das Hauptmenü der Datenbank)

    Dann oben in den Reitern auf Optionen und unten taucht ein Feld mit Globale Funktionen auf. Dort fügst Du folgenden Code ein:

    function contractDeleteInactive() do
        do as server
            for i in select 'Schüler' where Aktiv != true do
                delete (select Termine where contains(text(number(Vertrag.'Schüler')), text(i.Nr)))
            end
        end
    end;
    function contractDeleteOverEnd() do
        do as server
            for i in select Vertrag do
                delete (select Termine where date(Termin) > i.Ende)
            end
        end
    end;
    function contractSync() do
        do as server
            record(Dashboard,1).(serverTime := now())
        end;
        do as server
            let myDate := today();
            let myDay := format(myDate, "dd");
            let mySDay := 0;
            let myDays := 0;
            switch myDay do
            case "Mo":
                (mySDay := 1)
            case "Tu":
                (mySDay := 2)
            case "We":
                (mySDay := 3)
            case "Th":
                (mySDay := 4)
            case "Fr":
                (mySDay := 5)
            end;
            for i in select Vertrag where 'Schüler'.Aktiv and (Ende >= today() or Ende = null) do
                let myTermin := myDate;
                let myTDay := number(i.Wochentag);
                if myTDay = mySDay then
                    myTermin := myDate
                else
                    if myTDay > mySDay then
                        myDays := myTDay - mySDay;
                        myTermin := myTermin + myDays
                    else
                        if myTDay < mySDay then
                            myDays := mySDay - myTDay;
                            myTermin := myTermin - myDays + 7
                        end
                    end
                end;
                let myUhrzeit := i.Uhrzeit;
                let myDauer := i.Zeitdauer;
                for n from 0 to 365 step 7 do
                    let myUhrzeitStart := myUhrzeit;
                    let myUhrzeitEnd := myUhrzeit + myDauer;
                    if record(Dashboard,1).localTime > record(Dashboard,1).serverTime then
                        myUhrzeitStart := myUhrzeit - timeinterval(60 * 60 * 1000);
                        myUhrzeitEnd := myUhrzeit + myDauer - timeinterval(60 * 60 * 1000)
                    end;
                    let myCheck := true;
                    for m in select Termine do
                        if m.Termin = appointment(date(myTermin) + n + myUhrzeitStart, date(myTermin) + n + myUhrzeitEnd) and m.Vertrag = i then
                            myCheck := false
                        end
                    end;
                    if date(myTermin) + n > date(i.Ende) then
                        myCheck := false
                    end;
                    if myCheck then
                        let myRec := (create Termine);
                        myRec.(Vertrag := i.Nr);
                        myRec.(Termin := appointment(date(myTermin) + n + myUhrzeitStart, date(myTermin) + n + myUhrzeitEnd))
                    end
                end
            end
        end
    end;
    function syncTrigger() do
        record(Dashboard,1).(localTime := now());
        contractDeleteInactive();
        contractDeleteOverEnd();
        contractSync()
    end

    Als nächstes die Änderungen oben rechts speichern, damit die Funktionen geladen werden.

    Nun gehst Du eins weiter runter auf Trigger anch Öffnen (unter den globalen Funktionen) und fügst folgenden Code ein:

    let mySync := record(Dashboard,1).'Automatische Synchronisation';
    if mySync then syncTrigger() end;
    openRecord(record(Dashboard,1))

    Und zum Schluss noch in das Dashboard auf den Sync-Button mit folgendem Code:

    syncTrigger()

    Denke bitte daran, die entsprechenden Schüler noch auf Aktiv zu setzen.

    Wenn gewünscht, kannst Du in der Tabelle Vertrag jetzt wieder das Feld Unterrichtsdauer2 in Unterrichtsdauer umbenennen. Der Code übernimmt die Änderung im Hintergrund mit.

    Noch ein Tipp: Wenn Du ein Feld hast, was in der normalen Nutzerumgebung nicht sichtbar sein soll, wie im Dashboard z.B. localTime und serverTime oder unter Vertrag das neue Feld Zeitdauer, gehe in dem jeweiligen Feld auf Feld nur anzeigen, wenn: und füge folgendes ein:

    isAdminMode()

    Dann wird das Feld nur angezeigt, wenn Du den Admin-Modus aktiv hast.

    Ich habe Dir die Anpassung in deine Datenbank nochmal im Anhang beigefügt, da kannst Du ggf. gegenprüfen. Wichtig ist, dass Du die Reihenfolge bezüglich des Codes oben einhälst, da der Code sonst noch nicht alle neuen Felder erkennt.

    Ich hoffe es ist soweit verständlich. Viel Glück :-)

    • Manfred_Surges
    • vor 1 Jahr
    • Gemeldet - anzeigen

    Nochmal vielen Dank! Ich melde mich ob es klappt :-)

    • Manfred_Surges
    • vor 1 Jahr
    • Gemeldet - anzeigen

    Mit ein paar kleinen Anpassungen hat es denn geklappt! :-)

    Ist es auch möglich, einen einzelnen Termin per drag&drop zu verschieben?

    • Developer by Smartplanung
    • smartplanung
    • vor 1 Jahr
    • Gemeldet - anzeigen

    Das freut mich zu hören :-)

    Ich habe gerade nur ein iPad vor mir. Da geht Drag & Drop wohl nicht. Ob das grundsätzlich an einem PC geht weiß ich nicht. Bei einer Gantt-Ansicht in der Tabelle "Termine" ginge das. Man müsste dann aber generell den Code anpassen, da er sonst den leer gewordenen Termin-Slot wieder befüllt. Ich werde am Montag mal schauen, welche Möglichkeiten es da gibt.

    • Manfred_Surges
    • vor 1 Jahr
    • Gemeldet - anzeigen

    Das mit dem Drag& Drop wäre schon wichtig, wenn ich einen Schüler verschieben muss, der z.B krank ist. Notfalls sollte es auch möglich sein, den entsprechenden Termin per Tastatureingabe zu ändern. 

    Nach meinem bescheidenen Fachwissen sollte dann noch eine Abfrage, ob der Datensatz leer ist, gestellt werden, damit nur dann ein Jahr im Voraus automatisch Termine angelegt werden. (Es kommt vor, dass im Datensatz des Vertrags kein Endtermin vorgegeben ist!). Wenn schon Termine vorhanden sind, sollten dann jede Woche nach dem letzten Termin einer dran gehängt werden. 

    • Manfred_Surges
    • vor 1 Jahr
    • Gemeldet - anzeigen

    Es sollte auch möglich sein, einen Schüler grundsätzlich auf einen anderen Termin zu legen, wenn das im Vertrag geändert wird...bestehende künftige Termine müssten dann gelöscht werden. 

      • Developer by Smartplanung
      • smartplanung
      • vor 1 Jahr
      • Gemeldet - anzeigen

      Manfred Surges Hallo Manfred,

      ich habe jetzt nochmal einige Dinge für die genannten Anforderungen angepasst.

      Bitte wie folgt vorgehen:

      1. In der Tabelle Verträge folgende Felder anlegen: Textfeld = Wochentag_vorher ; Uhrzeit-Feld = Uhrzeit_vorher ; Zeitdauer-Feld = Zeitdauer_vorher
      2. In der Tabelle Termine folgende Felder anlegen: Termin = Ursprungstermin ; Ja/Nein-Feld = Changed, Standard auf Nein
      3. In der Tabelle Termine in dem Feld Termin unter Trigger nach Änderung folgendes eintragen:
        1. if Termin != Ursprungstermin then
              Changed := true
          end
        2.  
      4. Im Dashboard einen Button erstellen Einmalige Initialisierung. Inhalt:
        1. let myCnt := 0;
          for i in select 'Verträge' do
              i.(Wochentag_vorher := text(i.Wochentag));
              i.(Uhrzeit_vorher := i.Uhrzeit);
              i.(Zeitdauer_vorher := i.Zeitdauer);
              myCnt := myCnt + 1
          end;
          alert("Es wurden " + myCnt + " Verträge angepasst")
      5. Globale Funktionen komplett austauschen gegen:
        1. function contractDeleteInactive() do
              do as server
                  "//////////////////////////
          Lösche Termine von inaktiv gewordenen Schülern
          ///////////////////////////";
                  for i in select 'Schüler' where not Aktiv do
                      delete (select Termine where contains(text(number('Verträge'.'Schüler')), text(i.Nr)))
                  end
              end
          end;
          function contractDeleteOverEnd() do
              "//////////////////////////
          Lösche Termine, wenn Sie über ein Vertragsende hinaus gehen
          ///////////////////////////";
              for i in select 'Verträge' do
                  if i.Ende then
                      let myID := i;
                      let myEnde := i.Ende;
                      delete (select Termine)[not Changed and 'Verträge' = myID and date(Termin) > myEnde]
                  end
              end
          end;
          function contractDeleteChanged() do
              do as server
                  for i in select 'Verträge' do
                      let weekdayA := text(i.Wochentag_vorher);
                      let weekdayB := text(i.Wochentag);
                      let timeA := number(i.Uhrzeit_vorher);
                      let timeB := number(i.Uhrzeit);
                      let dauerA := number(i.Zeitdauer_vorher);
                      let dauerB := number(i.Zeitdauer);
                      if weekdayA != weekdayB or timeA != timeB or dauerA != dauerB then
                          delete (select Termine
                              where contains(text(number('Verträge'.'Schüler')), text(i.'Schüler')) and
                              date(Termin) >= today());
                          i.(Wochentag_vorher := text(i.Wochentag));
                          i.(Uhrzeit_vorher := i.Uhrzeit);
                          i.(Zeitdauer_vorher := i.Zeitdauer)
                      end
                  end
              end
          end;
          function contractSync() do
              do as server
                  "//////////////////////////
          Hole lokale Serverzeit für Prüfung der Zeitumstellung
          ///////////////////////////";
                  record(Dashboard,1).(serverTime := now())
              end;
              do as server
                  let myDate := today();
                  let myDay := format(myDate, "dd");
                  let mySDay := 0;
                  let myDays := 0;
                  "//////////////////////////
          Frage aktuellen Tag ab für die Berechnungen innerhalb einer Woche
          ///////////////////////////";
                  switch myDay do
                  case "Mo":
                      (mySDay := 1)
                  case "Tu":
                      (mySDay := 2)
                  case "We":
                      (mySDay := 3)
                  case "Th":
                      (mySDay := 4)
                  case "Fr":
                      (mySDay := 5)
                  end;
                  "//////////////////////////
          Select Schüler, welche aktiv sind und dessen Vertragsende in der Zukunft liegen oder nicht angegeben sind
          ///////////////////////////";
                  for i in select 'Verträge' where 'Schüler'.Aktiv and (Ende >= today() or Ende = null) do
                      let myTermin := myDate;
                      let myTDay := number(i.Wochentag);
                      record(Dashboard,1).(debug3 := myTDay + " - " + mySDay + " " + myDay);
                      "//////////////////////////
          Berechnung des ersten Tages, wo ein Termin gesetzt werden soll
          ///////////////////////////";
                      if myTDay = mySDay then
                          myTermin := myDate
                      else
                          if myTDay > mySDay then
                              myDays := myTDay - mySDay;
                              myTermin := myTermin + myDays
                          else
                              if myTDay < mySDay then
                                  myDays := mySDay - myTDay;
                                  myTermin := myTermin - myDays + 7
                              end
                          end
                      end;
                      record(Dashboard,1).(debug2 := myDays + " " + format(myTermin, "DD.MM.YYYY"));
                      let myUhrzeit := i.Uhrzeit;
                      let myDauer := i.Zeitdauer;
                      "//////////////////////////
          For-Schleife: Endpunkt 365. Tag (Tage im Jahr), in 7-Tages Schritten
          ///////////////////////////";
                      for n from 0 to 365 step 7 do
                          let myUhrzeitStart := myUhrzeit;
                          let myUhrzeitEnd := myUhrzeit + myDauer;
                          "//////////////////////////
          Abgleich und Anpassung der Zeitumstellung zwischen Server- und Lokalzeit
          ///////////////////////////";
                          if record(Dashboard,1).localTime > record(Dashboard,1).serverTime then
                              myUhrzeitStart := myUhrzeit - timeinterval(60 * 60 * 1000);
                              myUhrzeitEnd := myUhrzeit + myDauer - timeinterval(60 * 60 * 1000)
                          end;
                          "//////////////////////////
          Prüfen, ob Termin bereits vorhanden oder Schüler nachträglich Inaktiv. Wenn vorhanden oder Termin hinter Vertragsende: Überspringen
          ///////////////////////////";
                          let myCheck := true;
                          for m in select Termine do
                              let myTerminCheck := appointment(date(myTermin) + n + myUhrzeitStart, date(myTermin) + n + myUhrzeitEnd);
                              "//////////////////////////
          Prüfe zusätzlich, ob ein ursprünglicher Termin (laut Vertrag) vorhanden ist. Wenn ja, wurde er verändert und bleibt unberührt. Es wird auch kein Neuer Termin zum ursprünglichen Zeitpunkt erstellt.
          ///////////////////////////";
                              if m.Termin = m.'Verträge' = i and myTerminCheck or m.Ursprungstermin = myTerminCheck then
                                  myCheck := false
                              end
                          end;
                          "//////////////////////////
          Prüfen, ob Termin nach Vertragsende. Sonst ignorieren.
          ///////////////////////////";
                          if date(myTermin) + n > date(i.Ende) then
                              myCheck := false
                          end;
                          "//////////////////////////
          Wenn die Prüfungen kein 'false' ergeben haben, wird ein Termin erstellt
          ///////////////////////////";
                          if myCheck then
                              let myRec := (create Termine);
                              myRec.('Verträge' := i.Nr);
                              myRec.(Termin := appointment(date(myTermin) + n + myUhrzeitStart, date(myTermin) + n + myUhrzeitEnd));
                              myRec.(Ursprungstermin := myRec.Termin)
                          end
                      end
                  end
              end
          end;
          function syncTrigger() do
              record(Dashboard,1).(debug := null);
              record(Dashboard,1).(debug2 := null);
              record(Dashboard,1).(debug3 := null);
              record(Dashboard,1).(localTime := now());
              contractDeleteInactive();
              contractDeleteOverEnd();
              contractDeleteChanged();
              contractSync()
          end

      Führe den Button Einmalige Initialisierung aus, damit alle Verträge angepasst werden. Danach kannst Du den Button wieder löschen.

      Grundsätzlich: Bitte mach vorher ein Backup von der Datenbank. Es können immer Fehler auftreten, da ich selbst nicht den ganzen Umfang der Datenbank testen kann.

      Alternativ schick mir ein Backup deiner Datenbank per Mail an info@wilano.com, dann baue ich Dir das schnell ein und schicke dir die Datenbank zurück. Diese kannst Du dann ggf. fortan nutzen.

      • Ninox-Professional
      • planoxpro
      • vor 1 Jahr
      • Gemeldet - anzeigen

      Patrick W. Whow, das nenne ich mal Service! Ich habe mir das Script nicht näher angesehen, aber es ist schön dokumentiert und schaut nach viel Arbeit aus. 👍

      • Developer by Smartplanung
      • smartplanung
      • vor 1 Jahr
      • Gemeldet - anzeigen

      planox. pro Danke Axel, ich denke der Code könnte in vielen Dingen verbessert werden. Er ist tatsächlich aus dem Nichts entstanden und wurde nur bei Problemen geringfügig angepasst / optimiert. Ich habe ihn auch nochmal angepasst und Manfred zur Verfügung gestellt, da die Performance bei gewisser Anzahl an Datensätzen doch sehr unterirdisch war.

      Ich stelle den Code auch mal hier zu Doku-Zwecken mal rein. Vielleicht bringt jemandem die ein oder andere Herangehensweise näher zu seinem Ziel.

      function contractDeleteInactive() do
          do as server
              "//////////////////////////
      Lösche Termine von inaktiv gewordenen Schülern
      ///////////////////////////";
              let counter := 0;
              let countDel := 0;
              for i in (select Vertrag)[not Aktiv] do
                  let myVertrag := i.Nr;
                  delete (select Termine)[Vertrag = myVertrag];
                  countDel := cnt((select Termine)[Vertrag = myVertrag]);
                  counter := counter + 1
              end;
              record(Dashboard,1).(debugLog := debugLog +
                  "
      Verträge geprüft: " +
                  counter +
                  "
      Termine gelöscht: " +
                  countDel)
          end
      end;
      function contractDeleteOverEnd() do
          do as server
              "//////////////////////////
      Lösche Termine, wenn Sie über ein Vertragsende hinaus gehen
      ///////////////////////////";
              let counter := 0;
              let countDel := 0;
              for i in select Vertrag do
                  if i.Ende then
                      let myID := i;
                      let myEnde := i.Ende;
                      delete (select Termine)[not Changed and Vertrag = myID and date(Termin) > myEnde];
                      countDel := cnt((select Termine)[not Changed and Vertrag = myID and date(Termin) > myEnde]);
                      counter := counter + 1
                  end
              end;
              record(Dashboard,1).(debugLog := debugLog +
                  "
      Verträge mit Enddatum geprüft: " +
                  counter +
                  "
      Termine mit Enddatum gelöscht: " +
                  countDel)
          end
      end;
      function contractDeleteChanged() do
          do as server
              let counter := 0;
              let countDel := 0;
              for i in select Vertrag do
                  let weekdayA := text(i.Wochentag_vorher);
                  let weekdayB := text(i.Wochentag);
                  let timeA := number(i.Uhrzeit_vorher);
                  let timeB := number(i.Uhrzeit);
                  let dauerA := number(i.Zeitdauer_vorher);
                  let dauerB := number(i.Zeitdauer);
                  if weekdayA != weekdayB or timeA != timeB or dauerA != dauerB then
                      let myS := i.'Schüler';
                      delete (select Termine)[contains(text(number(Vertrag.'Schüler')), text(myS)) and date(Termin) >= today()];
                      countDel := cnt((select Termine)[contains(text(number(Vertrag.'Schüler')), text(myS)) and date(Termin) >= today()]);
                      i.(Wochentag_vorher := text(i.Wochentag));
                      i.(Uhrzeit_vorher := i.Uhrzeit);
                      i.(Zeitdauer_vorher := i.Zeitdauer);
                      counter := counter + 1
                  end
              end;
              record(Dashboard,1).(debugLog := debugLog +
                  "
      Geänderte Verträge geprüft: " +
                  counter +
                  "
      Geänderte Termine gelöscht: " +
                  countDel)
          end
      end;
      function contractSync() do
          do as server
              "//////////////////////////
      Hole lokale Serverzeit für Prüfung der Zeitumstellung
      ///////////////////////////";
              let counter := 0;
              let counter2 := 0;
              record(Dashboard,1).(serverTime := now());
              let myTimezone := false;
              if record(Dashboard,1).localTime > record(Dashboard,1).serverTime then
                  myTimezone := true
              end;
              let myDate := today();
              let myDay := format(myDate, "dd");
              let mySDay := 0;
              let myDays := 0;
              "//////////////////////////
      Frage aktuellen Tag ab für die Berechnungen innerhalb einer Woche
      ///////////////////////////";
              switch myDay do
              case "Mo":
                  (mySDay := 1)
              case "Tu":
                  (mySDay := 2)
              case "We":
                  (mySDay := 3)
              case "Th":
                  (mySDay := 4)
              case "Fr":
                  (mySDay := 5)
              end;
              "//////////////////////////
      Select Schüler, welche aktiv sind und dessen Vertragsende in der Zukunft liegen oder nicht angegeben sind
      ///////////////////////////";
              for i in (select Vertrag)[Aktiv and (Ende >= today() or Ende = null)] do
                  let myTermin := myDate;
                  let myTDay := number(i.Wochentag);
                  "//////////////////////////
      Berechnung des ersten Tages, wo ein Termin gesetzt werden soll
      ///////////////////////////";
                  if myTDay = mySDay then
                      myTermin := myDate
                  else
                      if myTDay > mySDay then
                          myDays := myTDay - mySDay;
                          myTermin := myTermin + myDays
                      else
                          if myTDay < mySDay then
                              myDays := mySDay - myTDay;
                              myTermin := myTermin - myDays + 7
                          end
                      end
                  end;
                  let myUhrzeit := i.Uhrzeit;
                  let myDauer := i.Zeitdauer;
                  "//////////////////////////
      For-Schleife: Endpunkt 365. Tag (Tage im Jahr), in 7-Tages Schritten
      ///////////////////////////";
                  for n from 0 to 365 step 7 do
                      let myUhrzeitStart := myUhrzeit;
                      let myUhrzeitEnd := myUhrzeit + myDauer;
                      "//////////////////////////
      Abgleich und Anpassung der Zeitumstellung zwischen Server- und Lokalzeit
      ///////////////////////////";
                      if myTimezone then
                          myUhrzeitStart := myUhrzeit - timeinterval(60 * 60 * 1000);
                          myUhrzeitEnd := myUhrzeit + myDauer - timeinterval(60 * 60 * 1000)
                      end;
                      "//////////////////////////
      Prüfen, ob Termin bereits vorhanden oder Schüler nachträglich Inaktiv. Wenn vorhanden oder Termin hinter Vertragsende: Überspringen
      ///////////////////////////";
                      let myCheck := true;
                      for m in select Termine do
                          let myTerminCheck := appointment(date(myTermin) + n + myUhrzeitStart, date(myTermin) + n + myUhrzeitEnd);
                          "//////////////////////////
      Prüfe zusätzlich, ob ein ursprünglicher Termin (laut Vertrag) vorhanden ist. Wenn ja, wurde er verändert und bleibt unberührt. Es wird auch kein Neuer Termin zum ursprünglichen Zeitpunkt erstellt.
      ///////////////////////////";
                          if m.Termin = m.Vertrag = i and myTerminCheck or m.Ursprungstermin = myTerminCheck then
                              myCheck := false
                          end
                      end;
                      "//////////////////////////
      Prüfen, ob Termin nach Vertragsende. Sonst ignorieren.
      ///////////////////////////";
                      if date(myTermin) + n > date(i.Ende) then
                          myCheck := false
                      end;
                      "//////////////////////////
      Wenn die Prüfungen kein 'false' ergeben haben, wird ein Termin erstellt
      ///////////////////////////";
                      if myCheck then
                          let myRec := (create Termine);
                          myRec.(Vertrag := i.Nr);
                          myRec.(Termin := appointment(date(myTermin) + n + myUhrzeitStart, date(myTermin) + n + myUhrzeitEnd));
                          myRec.(Ursprungstermin := myRec.Termin);
                          counter := counter + 1
                      end
                  end;
                  counter2 := counter2 + 1
              end;
              record(Dashboard,1).(debugLog := debugLog +
                  "
      Termine geprüft: " +
                  counter +
                  "
      Termine erstellt: " +
                  counter2)
          end
      end;
      function contractSmartSync() do
          do as server
              "//////////////////////////
      Hole lokale Serverzeit für Prüfung der Zeitumstellung
      ///////////////////////////";
              let counter := 0;
              let counter2 := 0;
              record(Dashboard,1).(serverTime := now());
              let myTimezone := false;
              if record(Dashboard,1).localTime > record(Dashboard,1).serverTime then
                  myTimezone := true
              end;
              let myDate := today();
              let myDay := format(myDate, "dd");
              let mySDay := 0;
              let myDays := 0;
              "//////////////////////////
      Frage aktuellen Tag ab für die Berechnungen innerhalb einer Woche
      ///////////////////////////";
              switch myDay do
              case "Mo":
                  (mySDay := 1)
              case "Tu":
                  (mySDay := 2)
              case "We":
                  (mySDay := 3)
              case "Th":
                  (mySDay := 4)
              case "Fr":
                  (mySDay := 5)
              end;
              "//////////////////////////
      Select Schüler, welche aktiv sind und dessen Vertragsende in der Zukunft liegen oder nicht angegeben sind
      ///////////////////////////";
              for i in (select Vertrag)[Aktiv and Changed and (Ende >= today() or Ende = null)] do
                  let myTermin := myDate;
                  let myTDay := number(i.Wochentag);
                  "//////////////////////////
      Berechnung des ersten Tages, wo ein Termin gesetzt werden soll
      ///////////////////////////";
                  if myTDay = mySDay then
                      myTermin := myDate
                  else
                      if myTDay > mySDay then
                          myDays := myTDay - mySDay;
                          myTermin := myTermin + myDays
                      else
                          if myTDay < mySDay then
                              myDays := mySDay - myTDay;
                              myTermin := myTermin - myDays + 7
                          end
                      end
                  end;
                  let myUhrzeit := i.Uhrzeit;
                  let myDauer := i.Zeitdauer;
                  "//////////////////////////
      For-Schleife: Endpunkt 365. Tag (Tage im Jahr), in 7-Tages Schritten
      ///////////////////////////";
                  for n from 0 to 365 step 7 do
                      let myUhrzeitStart := myUhrzeit;
                      let myUhrzeitEnd := myUhrzeit + myDauer;
                      "//////////////////////////
      Abgleich und Anpassung der Zeitumstellung zwischen Server- und Lokalzeit
      ///////////////////////////";
                      if myTimezone then
                          myUhrzeitStart := myUhrzeit - timeinterval(60 * 60 * 1000);
                          myUhrzeitEnd := myUhrzeit + myDauer - timeinterval(60 * 60 * 1000)
                      end;
                      "//////////////////////////
      Prüfen, ob Termin bereits vorhanden oder Schüler nachträglich Inaktiv. Wenn vorhanden oder Termin hinter Vertragsende: Überspringen
      ///////////////////////////";
                      let myCheck := true;
                      for m in select Termine do
                          let myTerminCheck := appointment(date(myTermin) + n + myUhrzeitStart, date(myTermin) + n + myUhrzeitEnd);
                          "//////////////////////////
      Prüfe zusätzlich, ob ein ursprünglicher Termin (laut Vertrag) vorhanden ist. Wenn ja, wurde er verändert und bleibt unberührt. Es wird auch kein Neuer Termin zum ursprünglichen Zeitpunkt erstellt.
      ///////////////////////////";
                          if m.Termin = m.Vertrag = i and myTerminCheck or m.Ursprungstermin = myTerminCheck then
                              myCheck := false
                          end
                      end;
                      "//////////////////////////
      Prüfen, ob Termin nach Vertragsende. Sonst ignorieren.
      ///////////////////////////";
                      if date(myTermin) + n > date(i.Ende) then
                          myCheck := false
                      end;
                      "//////////////////////////
      Wenn die Prüfungen kein 'false' ergeben haben, wird ein Termin erstellt
      ///////////////////////////";
                      if myCheck then
                          let myRec := (create Termine);
                          myRec.(Vertrag := i.Nr);
                          myRec.(Termin := appointment(date(myTermin) + n + myUhrzeitStart, date(myTermin) + n + myUhrzeitEnd));
                          myRec.(Ursprungstermin := myRec.Termin);
                          counter2 := counter2 + 1
                      end
                  end;
                  counter := counter + 1
              end;
              record(Dashboard,1).(debugLog := debugLog +
                  "
      Termine geprüft: " +
                  counter +
                  "
      Termine erstellt: " +
                  counter2)
          end
      end;
      function syncTrigger() do
          record(Dashboard,1).(debugLog := null);
          record(Dashboard,1).(localTime := now());
          record(Dashboard,1).(debugLog := "Start contractDeleteInactive");
          contractDeleteInactive();
          record(Dashboard,1).(debugLog := debugLog +
              "
      Finished contractDeleteInactive");
          record(Dashboard,1).(debugLog := debugLog +
              "
      Start contractDeleteOverEnd");
          contractDeleteOverEnd();
          record(Dashboard,1).(debugLog := debugLog +
              "
      Finished contractDeleteInactive");
          record(Dashboard,1).(debugLog := debugLog +
              "
      Start contractDeleteChanged");
          contractDeleteChanged();
          record(Dashboard,1).(debugLog := debugLog +
              "
      Finished contractDeleteChanged");
          if record(Dashboard,1).'Neu einlesen' then
              record(Dashboard,1).(debugLog := debugLog +
                  "
      Start contractSync");
              contractSync()
          else
              record(Dashboard,1).(debugLog := debugLog +
                  "
      Start contractSmartSync");
              contractSmartSync()
          end;
          record(Dashboard,1).(debugLog := debugLog +
              "
      Finished contractync")
      end
      
    • Manfred_Surges
    • vor 1 Jahr
    • Gemeldet - anzeigen

    Hallo Patrick, müssen die Anpassungen zusätzlich zu den bereits gemachten eingetragen werden, oder ersetzen sie die vorangegangenen komplett?

    • Manfred_Surges
    • vor 1 Jahr
    • Gemeldet - anzeigen

    Also, ich bekomm es nicht hin, da im Code Fehlermeldungen erscheinen. Ich schick mal eine Mail mit der Datenbank.