From ffc1cdee5165a738d455ed230e48dc53b4ac0bcc Mon Sep 17 00:00:00 2001
From: Eike Kettner <eike.kettner@posteo.de>
Date: Wed, 22 Apr 2020 21:53:35 +0200
Subject: [PATCH] Sort due items by their earliest due date

---
 .../docspell/joex/notify/MailTemplate.scala   |  2 +-
 .../joex/notify/NotifyDueItemsTask.scala      |  3 ++-
 .../restserver/conv/Conversions.scala         |  3 ++-
 .../scala/docspell/store/queries/QItem.scala  | 24 +++++++++++++++----
 4 files changed, 24 insertions(+), 8 deletions(-)

diff --git a/modules/joex/src/main/scala/docspell/joex/notify/MailTemplate.scala b/modules/joex/src/main/scala/docspell/joex/notify/MailTemplate.scala
index 7824f281..691867c0 100644
--- a/modules/joex/src/main/scala/docspell/joex/notify/MailTemplate.scala
+++ b/modules/joex/src/main/scala/docspell/joex/notify/MailTemplate.scala
@@ -26,7 +26,7 @@ this is Docspell informing you about due items coming up.
 {{/more}}
 
 
-Sincerly,
+Sincerely yours,
 Docspell
 """
 
diff --git a/modules/joex/src/main/scala/docspell/joex/notify/NotifyDueItemsTask.scala b/modules/joex/src/main/scala/docspell/joex/notify/NotifyDueItemsTask.scala
index de780e48..9b493654 100644
--- a/modules/joex/src/main/scala/docspell/joex/notify/NotifyDueItemsTask.scala
+++ b/modules/joex/src/main/scala/docspell/joex/notify/NotifyDueItemsTask.scala
@@ -73,7 +73,8 @@ object NotifyDueItemsTask {
           states = ItemState.validStates,
           tagsInclude = ctx.args.tagsInclude,
           tagsExclude = ctx.args.tagsExclude,
-          dueDateTo = Some(now + Duration.days(ctx.args.remindDays.toLong))
+          dueDateTo = Some(now + Duration.days(ctx.args.remindDays.toLong)),
+          orderAsc = Some(_.dueDate)
         )
       res <- ctx.store.transact(QItem.findItems(q).take(maxItems)).compile.toVector
     } yield res
diff --git a/modules/restserver/src/main/scala/docspell/restserver/conv/Conversions.scala b/modules/restserver/src/main/scala/docspell/restserver/conv/Conversions.scala
index f479912e..dd2f01f5 100644
--- a/modules/restserver/src/main/scala/docspell/restserver/conv/Conversions.scala
+++ b/modules/restserver/src/main/scala/docspell/restserver/conv/Conversions.scala
@@ -120,7 +120,8 @@ trait Conversions {
       m.dateFrom,
       m.dateUntil,
       m.dueDateFrom,
-      m.dueDateUntil
+      m.dueDateUntil,
+      None
     )
 
   def mkItemList(v: Vector[OItem.ListItem]): ItemLightList = {
diff --git a/modules/store/src/main/scala/docspell/store/queries/QItem.scala b/modules/store/src/main/scala/docspell/store/queries/QItem.scala
index a992e1b5..5d77cf95 100644
--- a/modules/store/src/main/scala/docspell/store/queries/QItem.scala
+++ b/modules/store/src/main/scala/docspell/store/queries/QItem.scala
@@ -9,6 +9,7 @@ import doobie.implicits._
 import docspell.common.{IdRef, _}
 import docspell.store.Store
 import docspell.store.records._
+import docspell.store.impl._
 import docspell.store.impl.Implicits._
 import org.log4s._
 
@@ -122,7 +123,8 @@ object QItem {
       dateFrom: Option[Timestamp],
       dateTo: Option[Timestamp],
       dueDateFrom: Option[Timestamp],
-      dueDateTo: Option[Timestamp]
+      dueDateTo: Option[Timestamp],
+      orderAsc: Option[RItem.Columns.type => Column]
   )
 
   object Query {
@@ -141,6 +143,7 @@ object QItem {
         None,
         None,
         None,
+        None,
         None
       )
   }
@@ -173,7 +176,13 @@ object QItem {
       PC.pid.prefix("p1").f,
       PC.name.prefix("p1").f,
       EC.eid.prefix("e1").f,
-      EC.name.prefix("e1").f
+      EC.name.prefix("e1").f,
+      q.orderAsc match {
+        case Some(co) =>
+          coalesce(co(IC).prefix("i").f, IC.created.prefix("i").f)
+        case None =>
+          IC.created.prefix("i").f
+      }
     )
 
     val withItem   = selectSimple(itemCols, RItem.table, IC.cid.is(q.collective))
@@ -258,9 +267,14 @@ object QItem {
       q.dueDateTo.map(d => IC.dueDate.prefix("i").isLt(d)).getOrElse(Fragment.empty)
     )
 
-    val order = orderBy(
-      coalesce(IC.itemDate.prefix("i").f, IC.created.prefix("i").f) ++ fr"DESC"
-    )
+    val order = q.orderAsc match {
+      case Some(co) =>
+        orderBy(coalesce(co(IC).prefix("i").f, IC.created.prefix("i").f) ++ fr"ASC")
+      case None =>
+        orderBy(
+          coalesce(IC.itemDate.prefix("i").f, IC.created.prefix("i").f) ++ fr"DESC"
+        )
+    }
     val frag = query ++ fr"WHERE" ++ cond ++ order
     logger.trace(s"List items: $frag")
     frag.query[ListItem].stream