<commit_before>/*                                                                           *\
*    ____                   ____  ___ __                                      *
*   / __ \____  ___  ____  / __ \/ (_) /_____  _____                          *
*  / / / / __ \/ _ \/ __ \/ / / / / / __/ __ \/ ___/   OpenOlitor             *
* / /_/ / /_/ /  __/ / / / /_/ / / / /_/ /_/ / /       contributed by tegonal *
* \____/ .___/\___/_/ /_/\____/_/_/\__/\____/_/        http://openolitor.ch   *
*     /_/                                                                     *
*                                                                             *
* This program is free software: you can redistribute it and/or modify it     *
* under the terms of the GNU General Public License as published by           *
* the Free Software Foundation, either version 3 of the License,              *
* or (at your option) any later version.                                      *
*                                                                             *
* This program is distributed in the hope that it will be useful, but         *
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  *
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for *
* more details.                                                               *
*                                                                             *
* You should have received a copy of the GNU General Public License along     *
* with this program. If not, see http://www.gnu.org/licenses/                 *
*                                                                             *
\*                                                                           */
package ch.openolitor.stammdaten.repositories

import ch.openolitor.core.models._
import scalikejdbc._
import sqls.{ distinct, count }
import ch.openolitor.core.db._
import ch.openolitor.core.repositories._
import ch.openolitor.core.repositories.BaseRepository._
import ch.openolitor.stammdaten.models._
import com.typesafe.scalalogging.LazyLogging
import ch.openolitor.buchhaltung.models._
import ch.openolitor.core.Macros._
import ch.openolitor.util.DateTimeUtil._
import org.joda.time.DateTime
import ch.openolitor.stammdaten.StammdatenDBMappings
import ch.openolitor.util.querybuilder.UriQueryParamToSQLSyntaxBuilder
import ch.openolitor.util.parsing.FilterExpr
import org.joda.time.LocalDate

trait StammdatenRepositoryQueries extends LazyLogging with StammdatenDBMappings {

  lazy val aboTyp = abotypMapping.syntax("atyp")
  lazy val person = personMapping.syntax("pers")
  lazy val lieferplanung = lieferplanungMapping.syntax("lieferplanung")
  lazy val lieferung = lieferungMapping.syntax("lieferung")
  lazy val lieferungJoin = lieferungMapping.syntax("lieferungJ")
  lazy val lieferposition = lieferpositionMapping.syntax("lieferposition")
  lazy val bestellung = bestellungMapping.syntax("bestellung")
  lazy val sammelbestellung = sammelbestellungMapping.syntax("sammelbestellung")
  lazy val bestellposition = bestellpositionMapping.syntax("bestellposition")
  lazy val kunde = kundeMapping.syntax("kunde")
  lazy val pendenz = pendenzMapping.syntax("pendenz")
  lazy val kundentyp = customKundentypMapping.syntax("kundentyp")
  lazy val postlieferung = postlieferungMapping.syntax("postlieferung")
  lazy val depotlieferung = depotlieferungMapping.syntax("depotlieferung")
  lazy val heimlieferung = heimlieferungMapping.syntax("heimlieferung")
  lazy val depot = depotMapping.syntax("depot")
  lazy val tour = tourMapping.syntax("tour")
  lazy val depotlieferungAbo = depotlieferungAboMapping.syntax("depotlieferungAbo")
  lazy val heimlieferungAbo = heimlieferungAboMapping.syntax("heimlieferungAbo")
  lazy val postlieferungAbo = postlieferungAboMapping.syntax("postlieferungAbo")
  lazy val produkt = produktMapping.syntax("produkt")
  lazy val produktekategorie = produktekategorieMapping.syntax("produktekategorie")
  lazy val produzent = produzentMapping.syntax("produzent")
  lazy val projekt = projektMapping.syntax("projekt")
  lazy val produktProduzent = produktProduzentMapping.syntax("produktProduzent")
  lazy val produktProduktekategorie = produktProduktekategorieMapping.syntax("produktProduktekategorie")
  lazy val abwesenheit = abwesenheitMapping.syntax("abwesenheit")
  lazy val korb = korbMapping.syntax("korb")
  lazy val vertrieb = vertriebMapping.syntax("vertrieb")
  lazy val tourlieferung = tourlieferungMapping.syntax("tourlieferung")
  lazy val depotAuslieferung = depotAuslieferungMapping.syntax("depotAuslieferung")
  lazy val tourAuslieferung = tourAuslieferungMapping.syntax("tourAuslieferung")
  lazy val postAuslieferung = postAuslieferungMapping.syntax("postAuslieferung")
  lazy val projektVorlage = projektVorlageMapping.syntax("projektVorlage")
  lazy val einladung = einladungMapping.syntax("einladung")

  lazy val lieferpositionShort = lieferpositionMapping.syntax
  lazy val korbShort = korbMapping.syntax

  protected def getAbotypenQuery(filter: Option[FilterExpr]) = {
    withSQL {
      select
        .from(abotypMapping as aboTyp)
        .where(UriQueryParamToSQLSyntaxBuilder.build(filter, aboTyp))
        .orderBy(aboTyp.name)
    }.map(abotypMapping(aboTyp)).list
  }

  protected def getKundenQuery = {
    withSQL {
      select
        .from(kundeMapping as kunde)
        .orderBy(kunde.bezeichnung)
    }.map(kundeMapping(kunde)).list
  }

  protected def getKundenUebersichtQuery(filter: Option[FilterExpr]) = {
    withSQL {
      select
        .from(kundeMapping as kunde)
        .leftJoin(personMapping as person).on(kunde.id, person.kundeId)
        .where(UriQueryParamToSQLSyntaxBuilder.build(filter, kunde))
        .orderBy(person.sort)
    }.one(kundeMapping(kunde))
      .toMany(
        rs => personMapping.opt(person)(rs)
      )
      .map((kunde, personen) => {
        val personenWihoutPwd = personen.toSet[Person].map(p => copyTo[Person, PersonSummary](p)).toSeq

        copyTo[Kunde, KundeUebersicht](kunde, "ansprechpersonen" -> personenWihoutPwd)
      }).list
  }

  protected def getCustomKundentypenQuery = {
    withSQL {
      select
        .from(customKundentypMapping as kundentyp)
        .orderBy(kundentyp.id)
    }.map(customKundentypMapping(kundentyp)).list
  }

  protected def getKundeDetailQuery(id: KundeId) = {
    withSQL {
      select
        .from(kundeMapping as kunde)
        .leftJoin(depotlieferungAboMapping as depotlieferungAbo).on(kunde.id, depotlieferungAbo.kundeId)
        .leftJoin(heimlieferungAboMapping as heimlieferungAbo).on(kunde.id, heimlieferungAbo.kundeId)
        .leftJoin(postlieferungAboMapping as postlieferungAbo).on(kunde.id, postlieferungAbo.kundeId)
        .leftJoin(personMapping as person).on(kunde.id, person.kundeId)
        .leftJoin(pendenzMapping as pendenz).on(kunde.id, pendenz.kundeId)
        .where.eq(kunde.id, parameter(id))
        .orderBy(person.sort)
    }.one(kundeMapping(kunde))
      .toManies(
        rs => postlieferungAboMapping.opt(postlieferungAbo)(rs),
        rs => heimlieferungAboMapping.opt(heimlieferungAbo)(rs),
        rs => depotlieferungAboMapping.opt(depotlieferungAbo)(rs),
        rs => personMapping.opt(person)(rs),
        rs => pendenzMapping.opt(pendenz)(rs)
      )
      .map((kunde, pl, hl, dl, personen, pendenzen) => {
        val abos = pl ++ hl ++ dl
        val personenWihoutPwd = personen.toSet[Person].map(p => copyTo[Person, PersonDetail](p)).toSeq

        copyTo[Kunde, KundeDetail](kunde, "abos" -> abos, "pendenzen" -> pendenzen, "ansprechpersonen" -> personenWihoutPwd)
      }).single
  }

  protected def getKundeDetailReportQuery(kundeId: KundeId, projekt: ProjektReport) = {
    val x = SubQuery.syntax("x").include(abwesenheit)
    withSQL {
      select
        .from(kundeMapping as kunde)
        .leftJoin(depotlieferungAboMapping as depotlieferungAbo).on(kunde.id, depotlieferungAbo.kundeId)
        .leftJoin(heimlieferungAboMapping as heimlieferungAbo).on(kunde.id, heimlieferungAbo.kundeId)
        .leftJoin(postlieferungAboMapping as postlieferungAbo).on(kunde.id, postlieferungAbo.kundeId)
        .leftJoin(personMapping as person).on(kunde.id, person.kundeId)
        .leftJoin(pendenzMapping as pendenz).on(kunde.id, pendenz.kundeId)
        .where.eq(kunde.id, parameter(kundeId))
        .orderBy(person.sort)
    }.one(kundeMapping(kunde))
      .toManies(
        rs => postlieferungAboMapping.opt(postlieferungAbo)(rs),
        rs => heimlieferungAboMapping.opt(heimlieferungAbo)(rs),
        rs => depotlieferungAboMapping.opt(depotlieferungAbo)(rs),
        rs => personMapping.opt(person)(rs),
        rs => pendenzMapping.opt(pendenz)(rs)
      )
      .map { (kunde, pl, hl, dl, personen, pendenzen) =>
        val abos = pl ++ hl ++ dl
        val personenWihoutPwd = personen.toSet[Person].map(p => copyTo[Person, PersonDetail](p)).toSeq

        copyTo[Kunde, KundeDetailReport](kunde, "abos" -> abos, "pendenzen" -> pendenzen,
          "personen" -> personenWihoutPwd, "projekt" -> projekt)
      }.single
  }

  protected def getPersonenQuery(kundeId: KundeId) = {
    withSQL {
      select
        .from(personMapping as person)
        .where.eq(person.kundeId, parameter(kundeId))
        .orderBy(person.sort)
    }.map(personMapping(person)).list
  }

  protected def getPersonenUebersichtQuery(filter: Option[FilterExpr]) = {
    withSQL {
      select
        .from(personMapping as person)
        .leftJoin(kundeMapping as kunde).on(person.kundeId, kunde.id)
        .where(UriQueryParamToSQLSyntaxBuilder.build(filter, person))
        .orderBy(person.name)
    }.one(personMapping(person))
      .toOne(
        rs => kundeMapping(kunde)(rs)
      ).map { (person, kunde) =>
          copyTo[Person, PersonUebersicht](
            person,
            "strasse" -> kunde.strasse,
            "hausNummer" -> kunde.hausNummer,
            "adressZusatz" -> kunde.adressZusatz,
            "plz" -> kunde.plz,
            "ort" -> kunde.ort,
            "kundentypen" -> kunde.typen,
            "kundenBemerkungen" -> kunde.bemerkungen
          )
        }.list
  }

  protected def getAbotypDetailQuery(id: AbotypId) = {
    withSQL {
      select
        .from(abotypMapping as aboTyp)
        .where.eq(aboTyp.id, parameter(id))
    }.map(abotypMapping(aboTyp)).single
  }

  protected def getDepotlieferungAbosQuery(abotypId: AbotypId) = {
    withSQL {
      select
        .from(depotlieferungAboMapping as depotlieferungAbo)
        .where.eq(depotlieferungAbo.abotypId, parameter(abotypId))
    }.map(depotlieferungAboMapping(depotlieferungAbo)).list
  }

  protected def getHeimlieferungAbosQuery(abotypId: AbotypId) = {
    withSQL {
      select
        .from(heimlieferungAboMapping as heimlieferungAbo)
        .where.eq(heimlieferungAbo.abotypId, parameter(abotypId))
    }.map(heimlieferungAboMapping(heimlieferungAbo)).list
  }

  protected def getPostlieferungAbosQuery(abotypId: AbotypId) = {
    withSQL {
      select
        .from(postlieferungAboMapping as postlieferungAbo)
        .where.eq(postlieferungAbo.abotypId, parameter(abotypId))
    }.map(postlieferungAboMapping(postlieferungAbo)).list
  }

  protected def getDepotlieferungAbosByVertriebQuery(vertriebId: VertriebId) = {
    withSQL {
      select
        .from(depotlieferungAboMapping as depotlieferungAbo)
        .where.eq(depotlieferungAbo.vertriebId, parameter(vertriebId))
    }.map(depotlieferungAboMapping(depotlieferungAbo)).list
  }

  protected def getHeimlieferungAbosByVertriebQuery(vertriebId: VertriebId) = {
    withSQL {
      select
        .from(heimlieferungAboMapping as heimlieferungAbo)
        .where.eq(heimlieferungAbo.vertriebId, parameter(vertriebId))
    }.map(heimlieferungAboMapping(heimlieferungAbo)).list
  }

  protected def getPostlieferungAbosByVertriebQuery(vertriebId: VertriebId) = {
    withSQL {
      select
        .from(postlieferungAboMapping as postlieferungAbo)
        .where.eq(postlieferungAbo.vertriebId, parameter(vertriebId))
    }.map(postlieferungAboMapping(postlieferungAbo)).list
  }

  protected def getDepotlieferungQuery(vertriebId: VertriebId) = {
    withSQL {
      select
        .from(depotlieferungMapping as depotlieferung)
        .leftJoin(depotMapping as depot).on(depotlieferung.depotId, depot.id)
        .where.eq(depotlieferung.vertriebId, parameter(vertriebId))
    }.one(depotlieferungMapping(depotlieferung)).toOne(depotMapping.opt(depot)).map { (vertriebsart, depot) =>
      val depotSummary = DepotSummary(depot.head.id, depot.head.name, depot.head.kurzzeichen)
      copyTo[Depotlieferung, DepotlieferungDetail](vertriebsart, "depot" -> depotSummary)
    }.list
  }

  protected def getDepotlieferungQuery(depotId: DepotId) = {
    withSQL {
      select
        .from(depotlieferungMapping as depotlieferung)
        .where.eq(depotlieferung.depotId, parameter(depotId))
    }.map(depotlieferungMapping(depotlieferung)).list
  }

  protected def getHeimlieferungQuery(vertriebId: VertriebId) = {
    withSQL {
      select
        .from(heimlieferungMapping as heimlieferung)
        .leftJoin(tourMapping as tour).on(heimlieferung.tourId, tour.id)
        .where.eq(heimlieferung.vertriebId, parameter(vertriebId))
    }.one(heimlieferungMapping(heimlieferung)).toOne(tourMapping.opt(tour)).map { (vertriebsart, tour) =>
      copyTo[Heimlieferung, HeimlieferungDetail](vertriebsart, "tour" -> tour.get)
    }.list
  }

  protected def getHeimlieferungQuery(tourId: TourId) = {
    withSQL {
      select
        .from(heimlieferungMapping as heimlieferung)
        .where.eq(heimlieferung.tourId, parameter(tourId))
    }.map(heimlieferungMapping(heimlieferung)).list
  }

  protected def getPostlieferungQuery(vertriebId: VertriebId) = {
    withSQL {
      select
        .from(postlieferungMapping as postlieferung)
        .where.eq(postlieferung.vertriebId, parameter(vertriebId))
    }.map { rs =>
      val pl = postlieferungMapping(postlieferung)(rs)
      copyTo[Postlieferung, PostlieferungDetail](pl)
    }.list
  }

  protected def getDepotlieferungQuery(vertriebsartId: VertriebsartId) = {
    withSQL {
      select
        .from(depotlieferungMapping as depotlieferung)
        .leftJoin(depotMapping as depot).on(depotlieferung.depotId, depot.id)
        .where.eq(depotlieferung.id, parameter(vertriebsartId))
    }.one(depotlieferungMapping(depotlieferung)).toOne(depotMapping.opt(depot)).map { (vertriebsart, depot) =>
      val depotSummary = DepotSummary(depot.head.id, depot.head.name, depot.head.kurzzeichen)
      copyTo[Depotlieferung, DepotlieferungDetail](vertriebsart, "depot" -> depotSummary)
    }.single
  }

  protected def getHeimlieferungQuery(vertriebsartId: VertriebsartId) = {
    withSQL {
      select
        .from(heimlieferungMapping as heimlieferung)
        .leftJoin(tourMapping as tour).on(heimlieferung.tourId, tour.id)
        .where.eq(heimlieferung.id, parameter(vertriebsartId))
    }.one(heimlieferungMapping(heimlieferung)).toOne(tourMapping.opt(tour)).map { (vertriebsart, tour) =>
      copyTo[Heimlieferung, HeimlieferungDetail](vertriebsart, "tour" -> tour.get)
    }.single
  }

  protected def getPostlieferungQuery(vertriebsartId: VertriebsartId) = {
    withSQL {
      select
        .from(postlieferungMapping as postlieferung)
        .where.eq(postlieferung.id, parameter(vertriebsartId))
    }.map { rs =>
      val pl = postlieferungMapping(postlieferung)(rs)
      copyTo[Postlieferung, PostlieferungDetail](pl)
    }.single
  }

  protected def getUngeplanteLieferungenQuery(abotypId: AbotypId, vertriebId: VertriebId) = {
    withSQL {
      select
        .from(lieferungMapping as lieferung)
        .leftJoin(vertriebMapping as vertrieb).on(lieferung.vertriebId, vertrieb.id)
        .where.eq(vertrieb.abotypId, parameter(abotypId)).and.eq(lieferung.vertriebId, parameter(vertriebId)).and.isNull(lieferung.lieferplanungId)
        .orderBy(lieferung.datum)
    }.map(lieferungMapping(lieferung)).list
  }

  protected def getUngeplanteLieferungenQuery(abotypId: AbotypId) = {
    withSQL {
      select
        .from(lieferungMapping as lieferung)
        .where.eq(lieferung.abotypId, parameter(abotypId)).and.isNull(lieferung.lieferplanungId)
        .orderBy(lieferung.datum)
    }.map(lieferungMapping(lieferung)).list
  }

  protected def getDepotsQuery = {
    withSQL {
      select
        .from(depotMapping as depot)
        .orderBy(depot.name)
    }.map(depotMapping(depot)).list
  }

  protected def getDepotDetailQuery(id: DepotId) = {
    withSQL {
      select
        .from(depotMapping as depot)
        .where.eq(depot.id, parameter(id))
    }.map(depotMapping(depot)).single
  }

  protected def getDepotDetailReportQuery(id: DepotId, projekt: ProjektReport) = {
    withSQL {
      select
        .from(depotMapping as depot)
        .leftJoin(depotlieferungAboMapping as depotlieferungAbo).on(depotlieferungAbo.depotId, depot.id)
        .leftJoin(kundeMapping as kunde).on(depotlieferungAbo.kundeId, kunde.id)
        .where.eq(depot.id, parameter(id))
    }
      .one(depotMapping(depot))
      .toManies(
        rs => depotlieferungAboMapping.opt(depotlieferungAbo)(rs),
        rs => kundeMapping.opt(kunde)(rs)
      )
      .map((depot, abos, kunden) => {
        val abosReport = abos.map { abo =>
          kunden.filter(_.id == abo.kundeId).headOption map { kunde =>
            val kundeReport = copyTo[Kunde, KundeReport](kunde)
            copyTo[DepotlieferungAbo, DepotlieferungAboReport](abo, "kundeReport" -> kundeReport)
          }
        }.flatten
        copyTo[Depot, DepotDetailReport](depot, "projekt" -> projekt, "abos" -> abosReport)
      }).single
  }

  protected def getDepotlieferungAbosQuery(filter: Option[FilterExpr]) = {
    withSQL {
      select
        .from(depotlieferungAboMapping as depotlieferungAbo)
        .where(UriQueryParamToSQLSyntaxBuilder.build(filter, depotlieferungAbo))
    }.map(depotlieferungAboMapping(depotlieferungAbo)).list
  }

  protected def getPersonenByDepotsQuery(filter: Option[FilterExpr]) = {
    withSQL {
      select
        .from(personMapping as person)
        .leftJoin(depotlieferungAboMapping as depotlieferungAbo).on(depotlieferungAbo.kundeId, person.kundeId)
        .leftJoin(depotMapping as depot).on(depotlieferungAbo.depotId, depot.id)
        .where(UriQueryParamToSQLSyntaxBuilder.build(filter, depot))
    }.map { rs =>
      copyTo[Person, PersonSummary](personMapping(person)(rs))
    }.list
  }

  protected def getPersonenAboAktivByDepotsQuery(filter: Option[FilterExpr]) = {
    withSQL {
      select
        .from(personMapping as person)
        .leftJoin(depotlieferungAboMapping as depotlieferungAbo).on(depotlieferungAbo.kundeId, person.kundeId)
        .leftJoin(depotMapping as depot).on(depotlieferungAbo.depotId, depot.id)
        .where.eq(depotlieferungAbo.aktiv, parameter(true)).and(UriQueryParamToSQLSyntaxBuilder.build(filter, depot))
    }.map { rs =>
      copyTo[Person, PersonSummary](personMapping(person)(rs))
    }.list
  }

  protected def getPersonenByTourenQuery(filter: Option[FilterExpr]) = {
    withSQL {
      select
        .from(personMapping as person)
        .leftJoin(heimlieferungAboMapping as heimlieferungAbo).on(heimlieferungAbo.kundeId, person.kundeId)
        .leftJoin(tourMapping as tour).on(heimlieferungAbo.tourId, tour.id)
        .where(UriQueryParamToSQLSyntaxBuilder.build(filter, tour))
    }.map { rs =>
      copyTo[Person, PersonSummary](personMapping(person)(rs))
    }.list
  }

  protected def getPersonenAboAktivByTourenQuery(filter: Option[FilterExpr]) = {
    withSQL {
      select
        .from(personMapping as person)
        .leftJoin(heimlieferungAboMapping as heimlieferungAbo).on(heimlieferungAbo.kundeId, person.kundeId)
        .leftJoin(tourMapping as tour).on(heimlieferungAbo.tourId, tour.id)
        .where.eq(heimlieferungAbo.aktiv, parameter(true)).and(UriQueryParamToSQLSyntaxBuilder.build(filter, tour))
    }.map { rs =>
      copyTo[Person, PersonSummary](personMapping(person)(rs))
    }.list
  }

  protected def getPersonenByAbotypenQuery(filter: Option[FilterExpr]) = {
    withSQL {
      select
        .from(personMapping as person)
        .leftJoin(depotlieferungAboMapping as depotlieferungAbo).on(depotlieferungAbo.kundeId, person.kundeId)
        .leftJoin(heimlieferungAboMapping as heimlieferungAbo).on(heimlieferungAbo.kundeId, person.kundeId)
        .leftJoin(postlieferungAboMapping as postlieferungAbo).on(postlieferungAbo.kundeId, person.kundeId)
        .leftJoin(abotypMapping as aboTyp).on(sqls.eq(depotlieferungAbo.abotypId, aboTyp.id).or.eq(heimlieferungAbo.abotypId, aboTyp.id).or.eq(postlieferungAbo.abotypId, aboTyp.id))
        .where(UriQueryParamToSQLSyntaxBuilder.build(filter, aboTyp))
    }.map { rs =>
      copyTo[Person, PersonSummary](personMapping(person)(rs))
    }.list
  }

  protected def getPersonenAboAktivByAbotypenQuery(filter: Option[FilterExpr]) = {
    withSQL {
      select
        .from(personMapping as person)
        .leftJoin(depotlieferungAboMapping as depotlieferungAbo).on(depotlieferungAbo.kundeId, person.kundeId)
        .leftJoin(heimlieferungAboMapping as heimlieferungAbo).on(heimlieferungAbo.kundeId, person.kundeId)
        .leftJoin(postlieferungAboMapping as postlieferungAbo).on(postlieferungAbo.kundeId, person.kundeId)
        .leftJoin(abotypMapping as aboTyp).on(sqls.eq(depotlieferungAbo.abotypId, aboTyp.id).or.eq(heimlieferungAbo.abotypId, aboTyp.id).or.eq(postlieferungAbo.abotypId, aboTyp.id))
        .where(UriQueryParamToSQLSyntaxBuilder.build(filter, aboTyp))
        .having(
          sqls.
            eq(depotlieferungAbo.aktiv, parameter(true))
            .or.eq(heimlieferungAbo.aktiv, parameter(true))
            .or.eq(postlieferungAbo.aktiv, parameter(true))
        )
    }.map { rs =>
      copyTo[Person, PersonSummary](personMapping(person)(rs))
    }.list
  }

  protected def getHeimlieferungAbosQuery(filter: Option[FilterExpr]) = {
    withSQL {
      select
        .from(heimlieferungAboMapping as heimlieferungAbo)
        .where(UriQueryParamToSQLSyntaxBuilder.build(filter, heimlieferungAbo))
    }.map(heimlieferungAboMapping(heimlieferungAbo)).list
  }

  protected def getPostlieferungAbosQuery(filter: Option[FilterExpr]) = {
    withSQL {
      select
        .from(postlieferungAboMapping as postlieferungAbo)
        .where(UriQueryParamToSQLSyntaxBuilder.build(filter, postlieferungAbo))
    }.map(postlieferungAboMapping(postlieferungAbo)).list
  }

  protected def getDepotlieferungAbosByDepotQuery(id: DepotId) = {
    withSQL {
      select
        .from(depotlieferungAboMapping as depotlieferungAbo)
        .where.eq(depotlieferungAbo.depotId, parameter(id))
    }.map(depotlieferungAboMapping(depotlieferungAbo)).list
  }

  protected def getDepotlieferungAboAusstehendQuery(id: AboId) = {
    getDepotlieferungAboBaseQuery(id, Some(()))
  }

  protected def getDepotlieferungAboQuery(id: AboId) = {
    getDepotlieferungAboBaseQuery(id, None)
  }

  private def getDepotlieferungAboBaseQuery(id: AboId, ausstehend: Option[Unit]) = {
    withSQL {
      select
        .from(depotlieferungAboMapping as depotlieferungAbo)
        .leftJoin(abwesenheitMapping as abwesenheit).on(depotlieferungAbo.id, abwesenheit.aboId)
        .leftJoin(abotypMapping as aboTyp).on(depotlieferungAbo.abotypId, aboTyp.id)
        .leftJoin(vertriebMapping as vertrieb).on(depotlieferungAbo.vertriebId, vertrieb.id)
        .leftJoin(lieferungMapping as lieferung).on(depotlieferungAbo.vertriebId, vertrieb.id)
        .leftJoin(lieferplanungMapping as lieferplanung).on(lieferung.lieferplanungId, lieferplanung.id)
        .where.eq(depotlieferungAbo.id, parameter(id))
        .and(sqls.toAndConditionOpt(
          ausstehend map (_ => sqls.isNull(lieferung.lieferplanungId).or.eq(lieferplanung.status, parameter(Ungeplant)).or.eq(lieferplanung.status, parameter(Offen)))
        ))
    }
      .one(depotlieferungAboMapping(depotlieferungAbo))
      .toManies(
        rs => abwesenheitMapping.opt(abwesenheit)(rs),
        rs => abotypMapping.opt(aboTyp)(rs),
        rs => vertriebMapping.opt(vertrieb)(rs),
        rs => lieferungMapping.opt(lieferung)(rs)
      )
      .map((abo, abw, aboTyp, vertriebe, lieferungen) => {
        val sortedAbw = abw.sortBy(_.datum)
        val sortedLieferungen = lieferungen.sortBy(_.datum)
        copyTo[DepotlieferungAbo, DepotlieferungAboDetail](abo, "abwesenheiten" -> sortedAbw, "lieferdaten" -> sortedLieferungen,
          "abotyp" -> aboTyp.headOption, "vertrieb" -> vertriebe.headOption)
      }).single
  }

  protected def getHeimlieferungAboAusstehendQuery(id: AboId) = {
    getHeimlieferungAboBaseQuery(id, Some(()))
  }

  protected def getHeimlieferungAboQuery(id: AboId) = {
    getHeimlieferungAboBaseQuery(id, None)
  }

  private def getHeimlieferungAboBaseQuery(id: AboId, ausstehend: Option[Unit]) = {
    withSQL {
      select
        .from(heimlieferungAboMapping as heimlieferungAbo)
        .leftJoin(abwesenheitMapping as abwesenheit).on(heimlieferungAbo.id, abwesenheit.aboId)
        .leftJoin(lieferplanungMapping as lieferplanung).on(lieferung.lieferplanungId, lieferplanung.id)
        .leftJoin(abotypMapping as aboTyp).on(heimlieferungAbo.abotypId, aboTyp.id)
        .leftJoin(vertriebMapping as vertrieb).on(heimlieferungAbo.vertriebId, vertrieb.id)
        .leftJoin(lieferungMapping as lieferung).on(heimlieferungAbo.vertriebId, vertrieb.id)
        .where.eq(heimlieferungAbo.id, parameter(id))
        .and(sqls.toAndConditionOpt(
          ausstehend map (_ => sqls.isNull(lieferung.lieferplanungId).or.eq(lieferplanung.status, parameter(Ungeplant)).or.eq(lieferplanung.status, parameter(Offen)))
        ))
    }.one(heimlieferungAboMapping(heimlieferungAbo))
      .toManies(
        rs => abwesenheitMapping.opt(abwesenheit)(rs),
        rs => abotypMapping.opt(aboTyp)(rs),
        rs => vertriebMapping.opt(vertrieb)(rs),
        rs => lieferungMapping.opt(lieferung)(rs)
      )
      .map((abo, abw, aboTyp, vertriebe, lieferungen) => {
        val sortedAbw = abw.sortBy(_.datum)
        val sortedLieferungen = lieferungen.sortBy(_.datum)
        copyTo[HeimlieferungAbo, HeimlieferungAboDetail](abo, "abwesenheiten" -> sortedAbw, "lieferdaten" -> sortedLieferungen,
          "abotyp" -> aboTyp.headOption, "vertrieb" -> vertriebe.headOption)
      }).single
  }

  protected def getPostlieferungAboAusstehendQuery(id: AboId) = {
    getPostlieferungAboBaseQuery(id, Some(()))
  }

  protected def getPostlieferungAboQuery(id: AboId) = {
    getPostlieferungAboBaseQuery(id, None)
  }

  private def getPostlieferungAboBaseQuery(id: AboId, ausstehend: Option[Unit]) = {
    withSQL {
      select
        .from(postlieferungAboMapping as postlieferungAbo)
        .leftJoin(abwesenheitMapping as abwesenheit).on(postlieferungAbo.id, abwesenheit.aboId)
        .leftJoin(lieferplanungMapping as lieferplanung).on(lieferung.lieferplanungId, lieferplanung.id)
        .leftJoin(abotypMapping as aboTyp).on(postlieferungAbo.abotypId, aboTyp.id)
        .leftJoin(vertriebMapping as vertrieb).on(postlieferungAbo.vertriebId, vertrieb.id)
        .leftJoin(lieferungMapping as lieferung).on(postlieferungAbo.vertriebId, vertrieb.id)
        .where.eq(postlieferungAbo.id, parameter(id))
        .and(sqls.toAndConditionOpt(
          ausstehend map (_ => sqls.isNull(lieferung.lieferplanungId).or.eq(lieferplanung.status, parameter(Ungeplant)).or.eq(lieferplanung.status, parameter(Offen)))
        ))
    }.one(postlieferungAboMapping(postlieferungAbo))
      .toManies(
        rs => abwesenheitMapping.opt(abwesenheit)(rs),
        rs => abotypMapping.opt(aboTyp)(rs),
        rs => vertriebMapping.opt(vertrieb)(rs),
        rs => lieferungMapping.opt(lieferung)(rs)
      )
      .map((abo, abw, aboTyp, vertriebe, lieferungen) => {
        val sortedAbw = abw.sortBy(_.datum)<commit_msg>Fix Lieferung-Join on wrong place<commit_after>/*                                                                           *\
*    ____                   ____  ___ __                                      *
*   / __ \____  ___  ____  / __ \/ (_) /_____  _____                          *
*  / / / / __ \/ _ \/ __ \/ / / / / / __/ __ \/ ___/   OpenOlitor             *
* / /_/ / /_/ /  __/ / / / /_/ / / / /_/ /_/ / /       contributed by tegonal *
* \____/ .___/\___/_/ /_/\____/_/_/\__/\____/_/        http://openolitor.ch   *
*     /_/                                                                     *
*                                                                             *
* This program is free software: you can redistribute it and/or modify it     *
* under the terms of the GNU General Public License as published by           *
* the Free Software Foundation, either version 3 of the License,              *
* or (at your option) any later version.                                      *
*                                                                             *
* This program is distributed in the hope that it will be useful, but         *
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  *
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for *
* more details.                                                               *
*                                                                             *
* You should have received a copy of the GNU General Public License along     *
* with this program. If not, see http://www.gnu.org/licenses/                 *
*                                                                             *
\*                                                                           */
package ch.openolitor.stammdaten.repositories

import ch.openolitor.core.models._
import scalikejdbc._
import sqls.{ distinct, count }
import ch.openolitor.core.db._
import ch.openolitor.core.repositories._
import ch.openolitor.core.repositories.BaseRepository._
import ch.openolitor.stammdaten.models._
import com.typesafe.scalalogging.LazyLogging
import ch.openolitor.buchhaltung.models._
import ch.openolitor.core.Macros._
import ch.openolitor.util.DateTimeUtil._
import org.joda.time.DateTime
import ch.openolitor.stammdaten.StammdatenDBMappings
import ch.openolitor.util.querybuilder.UriQueryParamToSQLSyntaxBuilder
import ch.openolitor.util.parsing.FilterExpr
import org.joda.time.LocalDate

trait StammdatenRepositoryQueries extends LazyLogging with StammdatenDBMappings {

  lazy val aboTyp = abotypMapping.syntax("atyp")
  lazy val person = personMapping.syntax("pers")
  lazy val lieferplanung = lieferplanungMapping.syntax("lieferplanung")
  lazy val lieferung = lieferungMapping.syntax("lieferung")
  lazy val lieferungJoin = lieferungMapping.syntax("lieferungJ")
  lazy val lieferposition = lieferpositionMapping.syntax("lieferposition")
  lazy val bestellung = bestellungMapping.syntax("bestellung")
  lazy val sammelbestellung = sammelbestellungMapping.syntax("sammelbestellung")
  lazy val bestellposition = bestellpositionMapping.syntax("bestellposition")
  lazy val kunde = kundeMapping.syntax("kunde")
  lazy val pendenz = pendenzMapping.syntax("pendenz")
  lazy val kundentyp = customKundentypMapping.syntax("kundentyp")
  lazy val postlieferung = postlieferungMapping.syntax("postlieferung")
  lazy val depotlieferung = depotlieferungMapping.syntax("depotlieferung")
  lazy val heimlieferung = heimlieferungMapping.syntax("heimlieferung")
  lazy val depot = depotMapping.syntax("depot")
  lazy val tour = tourMapping.syntax("tour")
  lazy val depotlieferungAbo = depotlieferungAboMapping.syntax("depotlieferungAbo")
  lazy val heimlieferungAbo = heimlieferungAboMapping.syntax("heimlieferungAbo")
  lazy val postlieferungAbo = postlieferungAboMapping.syntax("postlieferungAbo")
  lazy val produkt = produktMapping.syntax("produkt")
  lazy val produktekategorie = produktekategorieMapping.syntax("produktekategorie")
  lazy val produzent = produzentMapping.syntax("produzent")
  lazy val projekt = projektMapping.syntax("projekt")
  lazy val produktProduzent = produktProduzentMapping.syntax("produktProduzent")
  lazy val produktProduktekategorie = produktProduktekategorieMapping.syntax("produktProduktekategorie")
  lazy val abwesenheit = abwesenheitMapping.syntax("abwesenheit")
  lazy val korb = korbMapping.syntax("korb")
  lazy val vertrieb = vertriebMapping.syntax("vertrieb")
  lazy val tourlieferung = tourlieferungMapping.syntax("tourlieferung")
  lazy val depotAuslieferung = depotAuslieferungMapping.syntax("depotAuslieferung")
  lazy val tourAuslieferung = tourAuslieferungMapping.syntax("tourAuslieferung")
  lazy val postAuslieferung = postAuslieferungMapping.syntax("postAuslieferung")
  lazy val projektVorlage = projektVorlageMapping.syntax("projektVorlage")
  lazy val einladung = einladungMapping.syntax("einladung")

  lazy val lieferpositionShort = lieferpositionMapping.syntax
  lazy val korbShort = korbMapping.syntax

  protected def getAbotypenQuery(filter: Option[FilterExpr]) = {
    withSQL {
      select
        .from(abotypMapping as aboTyp)
        .where(UriQueryParamToSQLSyntaxBuilder.build(filter, aboTyp))
        .orderBy(aboTyp.name)
    }.map(abotypMapping(aboTyp)).list
  }

  protected def getKundenQuery = {
    withSQL {
      select
        .from(kundeMapping as kunde)
        .orderBy(kunde.bezeichnung)
    }.map(kundeMapping(kunde)).list
  }

  protected def getKundenUebersichtQuery(filter: Option[FilterExpr]) = {
    withSQL {
      select
        .from(kundeMapping as kunde)
        .leftJoin(personMapping as person).on(kunde.id, person.kundeId)
        .where(UriQueryParamToSQLSyntaxBuilder.build(filter, kunde))
        .orderBy(person.sort)
    }.one(kundeMapping(kunde))
      .toMany(
        rs => personMapping.opt(person)(rs)
      )
      .map((kunde, personen) => {
        val personenWihoutPwd = personen.toSet[Person].map(p => copyTo[Person, PersonSummary](p)).toSeq

        copyTo[Kunde, KundeUebersicht](kunde, "ansprechpersonen" -> personenWihoutPwd)
      }).list
  }

  protected def getCustomKundentypenQuery = {
    withSQL {
      select
        .from(customKundentypMapping as kundentyp)
        .orderBy(kundentyp.id)
    }.map(customKundentypMapping(kundentyp)).list
  }

  protected def getKundeDetailQuery(id: KundeId) = {
    withSQL {
      select
        .from(kundeMapping as kunde)
        .leftJoin(depotlieferungAboMapping as depotlieferungAbo).on(kunde.id, depotlieferungAbo.kundeId)
        .leftJoin(heimlieferungAboMapping as heimlieferungAbo).on(kunde.id, heimlieferungAbo.kundeId)
        .leftJoin(postlieferungAboMapping as postlieferungAbo).on(kunde.id, postlieferungAbo.kundeId)
        .leftJoin(personMapping as person).on(kunde.id, person.kundeId)
        .leftJoin(pendenzMapping as pendenz).on(kunde.id, pendenz.kundeId)
        .where.eq(kunde.id, parameter(id))
        .orderBy(person.sort)
    }.one(kundeMapping(kunde))
      .toManies(
        rs => postlieferungAboMapping.opt(postlieferungAbo)(rs),
        rs => heimlieferungAboMapping.opt(heimlieferungAbo)(rs),
        rs => depotlieferungAboMapping.opt(depotlieferungAbo)(rs),
        rs => personMapping.opt(person)(rs),
        rs => pendenzMapping.opt(pendenz)(rs)
      )
      .map((kunde, pl, hl, dl, personen, pendenzen) => {
        val abos = pl ++ hl ++ dl
        val personenWihoutPwd = personen.toSet[Person].map(p => copyTo[Person, PersonDetail](p)).toSeq

        copyTo[Kunde, KundeDetail](kunde, "abos" -> abos, "pendenzen" -> pendenzen, "ansprechpersonen" -> personenWihoutPwd)
      }).single
  }

  protected def getKundeDetailReportQuery(kundeId: KundeId, projekt: ProjektReport) = {
    val x = SubQuery.syntax("x").include(abwesenheit)
    withSQL {
      select
        .from(kundeMapping as kunde)
        .leftJoin(depotlieferungAboMapping as depotlieferungAbo).on(kunde.id, depotlieferungAbo.kundeId)
        .leftJoin(heimlieferungAboMapping as heimlieferungAbo).on(kunde.id, heimlieferungAbo.kundeId)
        .leftJoin(postlieferungAboMapping as postlieferungAbo).on(kunde.id, postlieferungAbo.kundeId)
        .leftJoin(personMapping as person).on(kunde.id, person.kundeId)
        .leftJoin(pendenzMapping as pendenz).on(kunde.id, pendenz.kundeId)
        .where.eq(kunde.id, parameter(kundeId))
        .orderBy(person.sort)
    }.one(kundeMapping(kunde))
      .toManies(
        rs => postlieferungAboMapping.opt(postlieferungAbo)(rs),
        rs => heimlieferungAboMapping.opt(heimlieferungAbo)(rs),
        rs => depotlieferungAboMapping.opt(depotlieferungAbo)(rs),
        rs => personMapping.opt(person)(rs),
        rs => pendenzMapping.opt(pendenz)(rs)
      )
      .map { (kunde, pl, hl, dl, personen, pendenzen) =>
        val abos = pl ++ hl ++ dl
        val personenWihoutPwd = personen.toSet[Person].map(p => copyTo[Person, PersonDetail](p)).toSeq

        copyTo[Kunde, KundeDetailReport](kunde, "abos" -> abos, "pendenzen" -> pendenzen,
          "personen" -> personenWihoutPwd, "projekt" -> projekt)
      }.single
  }

  protected def getPersonenQuery(kundeId: KundeId) = {
    withSQL {
      select
        .from(personMapping as person)
        .where.eq(person.kundeId, parameter(kundeId))
        .orderBy(person.sort)
    }.map(personMapping(person)).list
  }

  protected def getPersonenUebersichtQuery(filter: Option[FilterExpr]) = {
    withSQL {
      select
        .from(personMapping as person)
        .leftJoin(kundeMapping as kunde).on(person.kundeId, kunde.id)
        .where(UriQueryParamToSQLSyntaxBuilder.build(filter, person))
        .orderBy(person.name)
    }.one(personMapping(person))
      .toOne(
        rs => kundeMapping(kunde)(rs)
      ).map { (person, kunde) =>
          copyTo[Person, PersonUebersicht](
            person,
            "strasse" -> kunde.strasse,
            "hausNummer" -> kunde.hausNummer,
            "adressZusatz" -> kunde.adressZusatz,
            "plz" -> kunde.plz,
            "ort" -> kunde.ort,
            "kundentypen" -> kunde.typen,
            "kundenBemerkungen" -> kunde.bemerkungen
          )
        }.list
  }

  protected def getAbotypDetailQuery(id: AbotypId) = {
    withSQL {
      select
        .from(abotypMapping as aboTyp)
        .where.eq(aboTyp.id, parameter(id))
    }.map(abotypMapping(aboTyp)).single
  }

  protected def getDepotlieferungAbosQuery(abotypId: AbotypId) = {
    withSQL {
      select
        .from(depotlieferungAboMapping as depotlieferungAbo)
        .where.eq(depotlieferungAbo.abotypId, parameter(abotypId))
    }.map(depotlieferungAboMapping(depotlieferungAbo)).list
  }

  protected def getHeimlieferungAbosQuery(abotypId: AbotypId) = {
    withSQL {
      select
        .from(heimlieferungAboMapping as heimlieferungAbo)
        .where.eq(heimlieferungAbo.abotypId, parameter(abotypId))
    }.map(heimlieferungAboMapping(heimlieferungAbo)).list
  }

  protected def getPostlieferungAbosQuery(abotypId: AbotypId) = {
    withSQL {
      select
        .from(postlieferungAboMapping as postlieferungAbo)
        .where.eq(postlieferungAbo.abotypId, parameter(abotypId))
    }.map(postlieferungAboMapping(postlieferungAbo)).list
  }

  protected def getDepotlieferungAbosByVertriebQuery(vertriebId: VertriebId) = {
    withSQL {
      select
        .from(depotlieferungAboMapping as depotlieferungAbo)
        .where.eq(depotlieferungAbo.vertriebId, parameter(vertriebId))
    }.map(depotlieferungAboMapping(depotlieferungAbo)).list
  }

  protected def getHeimlieferungAbosByVertriebQuery(vertriebId: VertriebId) = {
    withSQL {
      select
        .from(heimlieferungAboMapping as heimlieferungAbo)
        .where.eq(heimlieferungAbo.vertriebId, parameter(vertriebId))
    }.map(heimlieferungAboMapping(heimlieferungAbo)).list
  }

  protected def getPostlieferungAbosByVertriebQuery(vertriebId: VertriebId) = {
    withSQL {
      select
        .from(postlieferungAboMapping as postlieferungAbo)
        .where.eq(postlieferungAbo.vertriebId, parameter(vertriebId))
    }.map(postlieferungAboMapping(postlieferungAbo)).list
  }

  protected def getDepotlieferungQuery(vertriebId: VertriebId) = {
    withSQL {
      select
        .from(depotlieferungMapping as depotlieferung)
        .leftJoin(depotMapping as depot).on(depotlieferung.depotId, depot.id)
        .where.eq(depotlieferung.vertriebId, parameter(vertriebId))
    }.one(depotlieferungMapping(depotlieferung)).toOne(depotMapping.opt(depot)).map { (vertriebsart, depot) =>
      val depotSummary = DepotSummary(depot.head.id, depot.head.name, depot.head.kurzzeichen)
      copyTo[Depotlieferung, DepotlieferungDetail](vertriebsart, "depot" -> depotSummary)
    }.list
  }

  protected def getDepotlieferungQuery(depotId: DepotId) = {
    withSQL {
      select
        .from(depotlieferungMapping as depotlieferung)
        .where.eq(depotlieferung.depotId, parameter(depotId))
    }.map(depotlieferungMapping(depotlieferung)).list
  }

  protected def getHeimlieferungQuery(vertriebId: VertriebId) = {
    withSQL {
      select
        .from(heimlieferungMapping as heimlieferung)
        .leftJoin(tourMapping as tour).on(heimlieferung.tourId, tour.id)
        .where.eq(heimlieferung.vertriebId, parameter(vertriebId))
    }.one(heimlieferungMapping(heimlieferung)).toOne(tourMapping.opt(tour)).map { (vertriebsart, tour) =>
      copyTo[Heimlieferung, HeimlieferungDetail](vertriebsart, "tour" -> tour.get)
    }.list
  }

  protected def getHeimlieferungQuery(tourId: TourId) = {
    withSQL {
      select
        .from(heimlieferungMapping as heimlieferung)
        .where.eq(heimlieferung.tourId, parameter(tourId))
    }.map(heimlieferungMapping(heimlieferung)).list
  }

  protected def getPostlieferungQuery(vertriebId: VertriebId) = {
    withSQL {
      select
        .from(postlieferungMapping as postlieferung)
        .where.eq(postlieferung.vertriebId, parameter(vertriebId))
    }.map { rs =>
      val pl = postlieferungMapping(postlieferung)(rs)
      copyTo[Postlieferung, PostlieferungDetail](pl)
    }.list
  }

  protected def getDepotlieferungQuery(vertriebsartId: VertriebsartId) = {
    withSQL {
      select
        .from(depotlieferungMapping as depotlieferung)
        .leftJoin(depotMapping as depot).on(depotlieferung.depotId, depot.id)
        .where.eq(depotlieferung.id, parameter(vertriebsartId))
    }.one(depotlieferungMapping(depotlieferung)).toOne(depotMapping.opt(depot)).map { (vertriebsart, depot) =>
      val depotSummary = DepotSummary(depot.head.id, depot.head.name, depot.head.kurzzeichen)
      copyTo[Depotlieferung, DepotlieferungDetail](vertriebsart, "depot" -> depotSummary)
    }.single
  }

  protected def getHeimlieferungQuery(vertriebsartId: VertriebsartId) = {
    withSQL {
      select
        .from(heimlieferungMapping as heimlieferung)
        .leftJoin(tourMapping as tour).on(heimlieferung.tourId, tour.id)
        .where.eq(heimlieferung.id, parameter(vertriebsartId))
    }.one(heimlieferungMapping(heimlieferung)).toOne(tourMapping.opt(tour)).map { (vertriebsart, tour) =>
      copyTo[Heimlieferung, HeimlieferungDetail](vertriebsart, "tour" -> tour.get)
    }.single
  }

  protected def getPostlieferungQuery(vertriebsartId: VertriebsartId) = {
    withSQL {
      select
        .from(postlieferungMapping as postlieferung)
        .where.eq(postlieferung.id, parameter(vertriebsartId))
    }.map { rs =>
      val pl = postlieferungMapping(postlieferung)(rs)
      copyTo[Postlieferung, PostlieferungDetail](pl)
    }.single
  }

  protected def getUngeplanteLieferungenQuery(abotypId: AbotypId, vertriebId: VertriebId) = {
    withSQL {
      select
        .from(lieferungMapping as lieferung)
        .leftJoin(vertriebMapping as vertrieb).on(lieferung.vertriebId, vertrieb.id)
        .where.eq(vertrieb.abotypId, parameter(abotypId)).and.eq(lieferung.vertriebId, parameter(vertriebId)).and.isNull(lieferung.lieferplanungId)
        .orderBy(lieferung.datum)
    }.map(lieferungMapping(lieferung)).list
  }

  protected def getUngeplanteLieferungenQuery(abotypId: AbotypId) = {
    withSQL {
      select
        .from(lieferungMapping as lieferung)
        .where.eq(lieferung.abotypId, parameter(abotypId)).and.isNull(lieferung.lieferplanungId)
        .orderBy(lieferung.datum)
    }.map(lieferungMapping(lieferung)).list
  }

  protected def getDepotsQuery = {
    withSQL {
      select
        .from(depotMapping as depot)
        .orderBy(depot.name)
    }.map(depotMapping(depot)).list
  }

  protected def getDepotDetailQuery(id: DepotId) = {
    withSQL {
      select
        .from(depotMapping as depot)
        .where.eq(depot.id, parameter(id))
    }.map(depotMapping(depot)).single
  }

  protected def getDepotDetailReportQuery(id: DepotId, projekt: ProjektReport) = {
    withSQL {
      select
        .from(depotMapping as depot)
        .leftJoin(depotlieferungAboMapping as depotlieferungAbo).on(depotlieferungAbo.depotId, depot.id)
        .leftJoin(kundeMapping as kunde).on(depotlieferungAbo.kundeId, kunde.id)
        .where.eq(depot.id, parameter(id))
    }
      .one(depotMapping(depot))
      .toManies(
        rs => depotlieferungAboMapping.opt(depotlieferungAbo)(rs),
        rs => kundeMapping.opt(kunde)(rs)
      )
      .map((depot, abos, kunden) => {
        val abosReport = abos.map { abo =>
          kunden.filter(_.id == abo.kundeId).headOption map { kunde =>
            val kundeReport = copyTo[Kunde, KundeReport](kunde)
            copyTo[DepotlieferungAbo, DepotlieferungAboReport](abo, "kundeReport" -> kundeReport)
          }
        }.flatten
        copyTo[Depot, DepotDetailReport](depot, "projekt" -> projekt, "abos" -> abosReport)
      }).single
  }

  protected def getDepotlieferungAbosQuery(filter: Option[FilterExpr]) = {
    withSQL {
      select
        .from(depotlieferungAboMapping as depotlieferungAbo)
        .where(UriQueryParamToSQLSyntaxBuilder.build(filter, depotlieferungAbo))
    }.map(depotlieferungAboMapping(depotlieferungAbo)).list
  }

  protected def getPersonenByDepotsQuery(filter: Option[FilterExpr]) = {
    withSQL {
      select
        .from(personMapping as person)
        .leftJoin(depotlieferungAboMapping as depotlieferungAbo).on(depotlieferungAbo.kundeId, person.kundeId)
        .leftJoin(depotMapping as depot).on(depotlieferungAbo.depotId, depot.id)
        .where(UriQueryParamToSQLSyntaxBuilder.build(filter, depot))
    }.map { rs =>
      copyTo[Person, PersonSummary](personMapping(person)(rs))
    }.list
  }

  protected def getPersonenAboAktivByDepotsQuery(filter: Option[FilterExpr]) = {
    withSQL {
      select
        .from(personMapping as person)
        .leftJoin(depotlieferungAboMapping as depotlieferungAbo).on(depotlieferungAbo.kundeId, person.kundeId)
        .leftJoin(depotMapping as depot).on(depotlieferungAbo.depotId, depot.id)
        .where.eq(depotlieferungAbo.aktiv, parameter(true)).and(UriQueryParamToSQLSyntaxBuilder.build(filter, depot))
    }.map { rs =>
      copyTo[Person, PersonSummary](personMapping(person)(rs))
    }.list
  }

  protected def getPersonenByTourenQuery(filter: Option[FilterExpr]) = {
    withSQL {
      select
        .from(personMapping as person)
        .leftJoin(heimlieferungAboMapping as heimlieferungAbo).on(heimlieferungAbo.kundeId, person.kundeId)
        .leftJoin(tourMapping as tour).on(heimlieferungAbo.tourId, tour.id)
        .where(UriQueryParamToSQLSyntaxBuilder.build(filter, tour))
    }.map { rs =>
      copyTo[Person, PersonSummary](personMapping(person)(rs))
    }.list
  }

  protected def getPersonenAboAktivByTourenQuery(filter: Option[FilterExpr]) = {
    withSQL {
      select
        .from(personMapping as person)
        .leftJoin(heimlieferungAboMapping as heimlieferungAbo).on(heimlieferungAbo.kundeId, person.kundeId)
        .leftJoin(tourMapping as tour).on(heimlieferungAbo.tourId, tour.id)
        .where.eq(heimlieferungAbo.aktiv, parameter(true)).and(UriQueryParamToSQLSyntaxBuilder.build(filter, tour))
    }.map { rs =>
      copyTo[Person, PersonSummary](personMapping(person)(rs))
    }.list
  }

  protected def getPersonenByAbotypenQuery(filter: Option[FilterExpr]) = {
    withSQL {
      select
        .from(personMapping as person)
        .leftJoin(depotlieferungAboMapping as depotlieferungAbo).on(depotlieferungAbo.kundeId, person.kundeId)
        .leftJoin(heimlieferungAboMapping as heimlieferungAbo).on(heimlieferungAbo.kundeId, person.kundeId)
        .leftJoin(postlieferungAboMapping as postlieferungAbo).on(postlieferungAbo.kundeId, person.kundeId)
        .leftJoin(abotypMapping as aboTyp).on(sqls.eq(depotlieferungAbo.abotypId, aboTyp.id).or.eq(heimlieferungAbo.abotypId, aboTyp.id).or.eq(postlieferungAbo.abotypId, aboTyp.id))
        .where(UriQueryParamToSQLSyntaxBuilder.build(filter, aboTyp))
    }.map { rs =>
      copyTo[Person, PersonSummary](personMapping(person)(rs))
    }.list
  }

  protected def getPersonenAboAktivByAbotypenQuery(filter: Option[FilterExpr]) = {
    withSQL {
      select
        .from(personMapping as person)
        .leftJoin(depotlieferungAboMapping as depotlieferungAbo).on(depotlieferungAbo.kundeId, person.kundeId)
        .leftJoin(heimlieferungAboMapping as heimlieferungAbo).on(heimlieferungAbo.kundeId, person.kundeId)
        .leftJoin(postlieferungAboMapping as postlieferungAbo).on(postlieferungAbo.kundeId, person.kundeId)
        .leftJoin(abotypMapping as aboTyp).on(sqls.eq(depotlieferungAbo.abotypId, aboTyp.id).or.eq(heimlieferungAbo.abotypId, aboTyp.id).or.eq(postlieferungAbo.abotypId, aboTyp.id))
        .where(UriQueryParamToSQLSyntaxBuilder.build(filter, aboTyp))
        .having(
          sqls.
            eq(depotlieferungAbo.aktiv, parameter(true))
            .or.eq(heimlieferungAbo.aktiv, parameter(true))
            .or.eq(postlieferungAbo.aktiv, parameter(true))
        )
    }.map { rs =>
      copyTo[Person, PersonSummary](personMapping(person)(rs))
    }.list
  }

  protected def getHeimlieferungAbosQuery(filter: Option[FilterExpr]) = {
    withSQL {
      select
        .from(heimlieferungAboMapping as heimlieferungAbo)
        .where(UriQueryParamToSQLSyntaxBuilder.build(filter, heimlieferungAbo))
    }.map(heimlieferungAboMapping(heimlieferungAbo)).list
  }

  protected def getPostlieferungAbosQuery(filter: Option[FilterExpr]) = {
    withSQL {
      select
        .from(postlieferungAboMapping as postlieferungAbo)
        .where(UriQueryParamToSQLSyntaxBuilder.build(filter, postlieferungAbo))
    }.map(postlieferungAboMapping(postlieferungAbo)).list
  }

  protected def getDepotlieferungAbosByDepotQuery(id: DepotId) = {
    withSQL {
      select
        .from(depotlieferungAboMapping as depotlieferungAbo)
        .where.eq(depotlieferungAbo.depotId, parameter(id))
    }.map(depotlieferungAboMapping(depotlieferungAbo)).list
  }

  protected def getDepotlieferungAboAusstehendQuery(id: AboId) = {
    getDepotlieferungAboBaseQuery(id, Some(()))
  }

  protected def getDepotlieferungAboQuery(id: AboId) = {
    getDepotlieferungAboBaseQuery(id, None)
  }

  private def getDepotlieferungAboBaseQuery(id: AboId, ausstehend: Option[Unit]) = {
    withSQL {
      select
        .from(depotlieferungAboMapping as depotlieferungAbo)
        .leftJoin(abwesenheitMapping as abwesenheit).on(depotlieferungAbo.id, abwesenheit.aboId)
        .leftJoin(abotypMapping as aboTyp).on(depotlieferungAbo.abotypId, aboTyp.id)
        .leftJoin(vertriebMapping as vertrieb).on(depotlieferungAbo.vertriebId, vertrieb.id)
        .leftJoin(lieferungMapping as lieferung).on(depotlieferungAbo.vertriebId, vertrieb.id)
        .leftJoin(lieferplanungMapping as lieferplanung).on(lieferung.lieferplanungId, lieferplanung.id)
        .where.eq(depotlieferungAbo.id, parameter(id))
        .and(sqls.toAndConditionOpt(
          ausstehend map (_ => sqls.isNull(lieferung.lieferplanungId).or.eq(lieferplanung.status, parameter(Ungeplant)).or.eq(lieferplanung.status, parameter(Offen)))
        ))
    }
      .one(depotlieferungAboMapping(depotlieferungAbo))
      .toManies(
        rs => abwesenheitMapping.opt(abwesenheit)(rs),
        rs => abotypMapping.opt(aboTyp)(rs),
        rs => vertriebMapping.opt(vertrieb)(rs),
        rs => lieferungMapping.opt(lieferung)(rs)
      )
      .map((abo, abw, aboTyp, vertriebe, lieferungen) => {
        val sortedAbw = abw.sortBy(_.datum)
        val sortedLieferungen = lieferungen.sortBy(_.datum)
        copyTo[DepotlieferungAbo, DepotlieferungAboDetail](abo, "abwesenheiten" -> sortedAbw, "lieferdaten" -> sortedLieferungen,
          "abotyp" -> aboTyp.headOption, "vertrieb" -> vertriebe.headOption)
      }).single
  }

  protected def getHeimlieferungAboAusstehendQuery(id: AboId) = {
    getHeimlieferungAboBaseQuery(id, Some(()))
  }

  protected def getHeimlieferungAboQuery(id: AboId) = {
    getHeimlieferungAboBaseQuery(id, None)
  }

  private def getHeimlieferungAboBaseQuery(id: AboId, ausstehend: Option[Unit]) = {
    withSQL {
      select
        .from(heimlieferungAboMapping as heimlieferungAbo)
        .leftJoin(abwesenheitMapping as abwesenheit).on(heimlieferungAbo.id, abwesenheit.aboId)
        .leftJoin(abotypMapping as aboTyp).on(heimlieferungAbo.abotypId, aboTyp.id)
        .leftJoin(vertriebMapping as vertrieb).on(heimlieferungAbo.vertriebId, vertrieb.id)
        .leftJoin(lieferungMapping as lieferung).on(heimlieferungAbo.vertriebId, vertrieb.id)
        .leftJoin(lieferplanungMapping as lieferplanung).on(lieferung.lieferplanungId, lieferplanung.id)
        .where.eq(heimlieferungAbo.id, parameter(id))
        .and(sqls.toAndConditionOpt(
          ausstehend map (_ => sqls.isNull(lieferung.lieferplanungId).or.eq(lieferplanung.status, parameter(Ungeplant)).or.eq(lieferplanung.status, parameter(Offen)))
        ))
    }.one(heimlieferungAboMapping(heimlieferungAbo))
      .toManies(
        rs => abwesenheitMapping.opt(abwesenheit)(rs),
        rs => abotypMapping.opt(aboTyp)(rs),
        rs => vertriebMapping.opt(vertrieb)(rs),
        rs => lieferungMapping.opt(lieferung)(rs)
      )
      .map((abo, abw, aboTyp, vertriebe, lieferungen) => {
        val sortedAbw = abw.sortBy(_.datum)
        val sortedLieferungen = lieferungen.sortBy(_.datum)
        copyTo[HeimlieferungAbo, HeimlieferungAboDetail](abo, "abwesenheiten" -> sortedAbw, "lieferdaten" -> sortedLieferungen,
          "abotyp" -> aboTyp.headOption, "vertrieb" -> vertriebe.headOption)
      }).single
  }

  protected def getPostlieferungAboAusstehendQuery(id: AboId) = {
    getPostlieferungAboBaseQuery(id, Some(()))
  }

  protected def getPostlieferungAboQuery(id: AboId) = {
    getPostlieferungAboBaseQuery(id, None)
  }

  private def getPostlieferungAboBaseQuery(id: AboId, ausstehend: Option[Unit]) = {
    withSQL {
      select
        .from(postlieferungAboMapping as postlieferungAbo)
        .leftJoin(abwesenheitMapping as abwesenheit).on(postlieferungAbo.id, abwesenheit.aboId)
        .leftJoin(abotypMapping as aboTyp).on(postlieferungAbo.abotypId, aboTyp.id)
        .leftJoin(vertriebMapping as vertrieb).on(postlieferungAbo.vertriebId, vertrieb.id)
        .leftJoin(lieferungMapping as lieferung).on(postlieferungAbo.vertriebId, vertrieb.id)
        .leftJoin(lieferplanungMapping as lieferplanung).on(lieferung.lieferplanungId, lieferplanung.id)
        .where.eq(postlieferungAbo.id, parameter(id))
        .and(sqls.toAndConditionOpt(
          ausstehend map (_ => sqls.isNull(lieferung.lieferplanungId).or.eq(lieferplanung.status, parameter(Ungeplant)).or.eq(lieferplanung.status, parameter(Offen)))
        ))
    }.one(postlieferungAboMapping(postlieferungAbo))
      .toManies(
        rs => abwesenheitMapping.opt(abwesenheit)(rs),
        rs => abotypMapping.opt(aboTyp)(rs),
        rs => vertriebMapping.opt(vertrieb)(rs),
        rs => lieferungMapping.opt(lieferung)(rs)
      )
      .map((abo, abw, aboTyp, vertriebe, lieferungen) => {
        val sortedAbw = abw.sortBy(_.datum)
{'fraction_non_alphanumeric': 0.10643564356435643, 'fraction_numerical': 3.561507229859677e-05, 'mean_word_length': 3.300582018685863, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 171, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 52, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7063659', 'n_tokens_mistral': 19415, 'n_tokens_neox': 19189, 'n_words': 2751}