diff --git a/modules/common/src/main/scala/docspell/common/ItemState.scala b/modules/common/src/main/scala/docspell/common/ItemState.scala index 506e803d..77766295 100644 --- a/modules/common/src/main/scala/docspell/common/ItemState.scala +++ b/modules/common/src/main/scala/docspell/common/ItemState.scala @@ -1,11 +1,18 @@ package docspell.common import io.circe.{Decoder, Encoder} +import cats.data.NonEmptyList sealed trait ItemState { self: Product => final def name: String = productPrefix.toLowerCase + + def isValid: Boolean = + ItemState.validStates.exists(_ == this) + + def isInvalid: Boolean = + ItemState.invalidStates.exists(_ == this) } object ItemState { @@ -24,8 +31,11 @@ object ItemState { case _ => Left(s"Invalid item state: $str") } - val validStates: Seq[ItemState] = - Seq(Created, Confirmed) + val validStates: NonEmptyList[ItemState] = + NonEmptyList.of(Created, Confirmed) + + val invalidStates: NonEmptyList[ItemState] = + NonEmptyList.of(Premature, Processing) def unsafe(str: String): ItemState = fromString(str).fold(sys.error, identity) 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 12c0d436..2b789ff1 100644 --- a/modules/joex/src/main/scala/docspell/joex/notify/NotifyDueItemsTask.scala +++ b/modules/joex/src/main/scala/docspell/joex/notify/NotifyDueItemsTask.scala @@ -71,7 +71,7 @@ object NotifyDueItemsTask { QItem.Query .empty(ctx.args.account.collective) .copy( - states = ItemState.validStates, + states = ItemState.validStates.toList, tagsInclude = ctx.args.tagsInclude, tagsExclude = ctx.args.tagsExclude, dueDateFrom = ctx.args.daysBack.map(back => now - Duration.days(back.toLong)), diff --git a/modules/joex/src/main/scala/docspell/joex/process/ItemHandler.scala b/modules/joex/src/main/scala/docspell/joex/process/ItemHandler.scala index 0ce3f9b5..4334ece4 100644 --- a/modules/joex/src/main/scala/docspell/joex/process/ItemHandler.scala +++ b/modules/joex/src/main/scala/docspell/joex/process/ItemHandler.scala @@ -25,7 +25,11 @@ object ItemHandler { def itemStateTask[F[_]: Sync, A]( state: ItemState )(data: ItemData): Task[F, A, ItemData] = - Task(ctx => ctx.store.transact(RItem.updateState(data.item.id, state)).map(_ => data)) + Task(ctx => + ctx.store + .transact(RItem.updateState(data.item.id, state, ItemState.invalidStates)) + .map(_ => data) + ) def isLastRetry[F[_]: Sync, A](ctx: Context[F, A]): F[Boolean] = for { diff --git a/modules/joex/src/main/scala/docspell/joex/process/LinkProposal.scala b/modules/joex/src/main/scala/docspell/joex/process/LinkProposal.scala index 7552b8db..a6d7bc16 100644 --- a/modules/joex/src/main/scala/docspell/joex/process/LinkProposal.scala +++ b/modules/joex/src/main/scala/docspell/joex/process/LinkProposal.scala @@ -9,21 +9,26 @@ import docspell.store.records.RItem object LinkProposal { def apply[F[_]: Sync](data: ItemData): Task[F, ProcessItemArgs, ItemData] = - Task { ctx => - // sort by weight; order of equal weights is not important, just - // choose one others are then suggestions - // doc-date is only set when given explicitely, not from "guessing" - val proposals = MetaProposalList - .flatten(data.metas.map(_.proposals)) - .filter(_.proposalType != MetaProposalType.DocDate) - .sortByWeights + if (data.item.state.isValid) + Task + .log[F, ProcessItemArgs](_.debug(s"Not linking proposals on existing item")) + .map(_ => data) + else + Task { ctx => + // sort by weight; order of equal weights is not important, just + // choose one others are then suggestions + // doc-date is only set when given explicitely, not from "guessing" + val proposals = MetaProposalList + .flatten(data.metas.map(_.proposals)) + .filter(_.proposalType != MetaProposalType.DocDate) + .sortByWeights - ctx.logger.info(s"Starting linking proposals") *> - MetaProposalType.all - .traverse(applyValue(data, proposals, ctx)) - .map(result => ctx.logger.info(s"Results from proposal processing: $result")) - .map(_ => data) - } + ctx.logger.info(s"Starting linking proposals") *> + MetaProposalType.all + .traverse(applyValue(data, proposals, ctx)) + .map(result => ctx.logger.info(s"Results from proposal processing: $result")) + .map(_ => data) + } def applyValue[F[_]: Sync]( data: ItemData, @@ -40,8 +45,9 @@ object LinkProposal { Result.single(mpt) ) case Some(a) => + val ids = a.values.map(_.ref.id.id) ctx.logger.info( - s"Found many (${a.size}, ${a.values.map(_.ref.id.id)}) candidates for ${a.proposalType}. Setting first." + s"Found many (${a.size}, ${ids}) candidates for ${a.proposalType}. Setting first." ) *> setItemMeta(data.item.id, ctx, a.proposalType, a.values.head.ref.id).map(_ => Result.multiple(mpt) 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 ea262bce..125d6ae0 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/conv/Conversions.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/conv/Conversions.scala @@ -109,7 +109,7 @@ trait Conversions { coll, m.name, if (m.inbox) Seq(ItemState.Created) - else ItemState.validStates, + else ItemState.validStates.toList, m.direction, m.corrPerson, m.corrOrg, diff --git a/modules/store/src/main/scala/docspell/store/records/RItem.scala b/modules/store/src/main/scala/docspell/store/records/RItem.scala index 756ef292..05f06e9f 100644 --- a/modules/store/src/main/scala/docspell/store/records/RItem.scala +++ b/modules/store/src/main/scala/docspell/store/records/RItem.scala @@ -1,7 +1,8 @@ package docspell.store.records -import cats.implicits._ +import cats.data.NonEmptyList import cats.effect.Sync +import cats.implicits._ import doobie._ import doobie.implicits._ import docspell.common._ @@ -110,12 +111,16 @@ object RItem { def getCollective(itemId: Ident): ConnectionIO[Option[Ident]] = selectSimple(List(cid), table, id.is(itemId)).query[Ident].option - def updateState(itemId: Ident, itemState: ItemState): ConnectionIO[Int] = + def updateState( + itemId: Ident, + itemState: ItemState, + existing: NonEmptyList[ItemState] + ): ConnectionIO[Int] = for { t <- currentTime n <- updateRow( table, - id.is(itemId), + and(id.is(itemId), state.isIn(existing)), commas(state.setTo(itemState), updated.setTo(t)) ).update.run } yield n