diff --git a/ganttproject/src/main/java/biz/ganttproject/customproperty/CalculatedPropertyUpdater.kt b/ganttproject/src/main/java/biz/ganttproject/customproperty/CalculatedPropertyUpdater.kt index 8926bd8e28..cc8edb0f63 100644 --- a/ganttproject/src/main/java/biz/ganttproject/customproperty/CalculatedPropertyUpdater.kt +++ b/ganttproject/src/main/java/biz/ganttproject/customproperty/CalculatedPropertyUpdater.kt @@ -38,7 +38,9 @@ class CalculatedPropertyUpdater( else -> null } } + projectDatabase.mapTasks(*(updaters.toTypedArray())) + projectDatabase.updateBuiltInCalculatedColumns() LOG.debug("<<<") } } diff --git a/ganttproject/src/main/java/net/sourceforge/ganttproject/storage/LazyProjectDatabaseProxy.kt b/ganttproject/src/main/java/net/sourceforge/ganttproject/storage/LazyProjectDatabaseProxy.kt index 672c1bcc03..df7fdc8049 100644 --- a/ganttproject/src/main/java/net/sourceforge/ganttproject/storage/LazyProjectDatabaseProxy.kt +++ b/ganttproject/src/main/java/net/sourceforge/ganttproject/storage/LazyProjectDatabaseProxy.kt @@ -50,6 +50,11 @@ class LazyProjectDatabaseProxy( }.toMap() } ) } + + override fun updateBuiltInCalculatedColumns() { + getDatabase().updateBuiltInCalculatedColumns() + } + private val projectEventListenerImpl by lazy { ProjectEventListenerImpl(this, taskManager, calculatedPropertyUpdater, filterUpdater) } private fun isInitialized(): Boolean = lazyProjectDatabase != null diff --git a/ganttproject/src/main/java/net/sourceforge/ganttproject/storage/ProjectDatabase.kt b/ganttproject/src/main/java/net/sourceforge/ganttproject/storage/ProjectDatabase.kt index ae4ba05b91..936ffb9495 100644 --- a/ganttproject/src/main/java/net/sourceforge/ganttproject/storage/ProjectDatabase.kt +++ b/ganttproject/src/main/java/net/sourceforge/ganttproject/storage/ProjectDatabase.kt @@ -167,4 +167,6 @@ interface ProjectDatabase { * @param customPropertyManager an instance of the CustomPropertyManager with the actual custom property definitions. */ fun onCustomColumnChange(customPropertyManager: CustomPropertyManager) + + fun updateBuiltInCalculatedColumns() } diff --git a/ganttproject/src/main/java/net/sourceforge/ganttproject/storage/SqlProjectDatabaseImpl.kt b/ganttproject/src/main/java/net/sourceforge/ganttproject/storage/SqlProjectDatabaseImpl.kt index 75ed3357f1..da56c232f4 100644 --- a/ganttproject/src/main/java/net/sourceforge/ganttproject/storage/SqlProjectDatabaseImpl.kt +++ b/ganttproject/src/main/java/net/sourceforge/ganttproject/storage/SqlProjectDatabaseImpl.kt @@ -84,6 +84,10 @@ class SqlProjectDatabaseImpl( override fun onCustomColumnChange(customPropertyManager: CustomPropertyManager) = customPropertyStorageManager.onCustomColumnChange(customPropertyManager) + override fun updateBuiltInCalculatedColumns() { + runScriptFromResource(dataSource, DB_UPDATE_BUILTIN_CALCULATED_COLUMNS) + } + /** * Applies updates from Colloboque */ @@ -538,4 +542,5 @@ const val SQL_PROJECT_DATABASE_OPTIONS = ";DB_CLOSE_DELAY=-1;DATABASE_TO_LOWER=t private const val H2_IN_MEMORY_URL = "jdbc:h2:mem:gantt-project-state$SQL_PROJECT_DATABASE_OPTIONS" private const val DB_INIT_SCRIPT_PATH = "/sql/init-project-database.sql" private const val DB_INIT_SCRIPT_PATH2 = "/sql/init-project-database-step2.sql" +private const val DB_UPDATE_BUILTIN_CALCULATED_COLUMNS = "/sql/update-builtin-calculated-columns.sql" private val LOG = GPLogger.create("ProjectDatabase") diff --git a/ganttproject/src/main/resources/resources/sql/init-project-database-step2.sql b/ganttproject/src/main/resources/resources/sql/init-project-database-step2.sql index 57657884e8..3f4f1f7b3f 100644 --- a/ganttproject/src/main/resources/resources/sql/init-project-database-step2.sql +++ b/ganttproject/src/main/resources/resources/sql/init-project-database-step2.sql @@ -1,5 +1,5 @@ --- CREATE ALIAS IF NOT EXISTS TASK_COST FOR "net.sourceforge.ganttproject.storage.H2FunctionsKt.taskCost"; --- CREATE ALIAS IF NOT EXISTS TASK_END_DATE FOR "net.sourceforge.ganttproject.storage.H2FunctionsKt.taskEndDate"; +CREATE ALIAS IF NOT EXISTS TASK_COST FOR "net.sourceforge.ganttproject.storage.H2FunctionsKt.taskCost"; +CREATE ALIAS IF NOT EXISTS TASK_END_DATE FOR "net.sourceforge.ganttproject.storage.H2FunctionsKt.taskEndDate"; -- -- DROP VIEW TaskViewForComputedColumns; -- DROP TABLE Task CASCADE ; diff --git a/ganttproject/src/main/resources/resources/sql/update-builtin-calculated-columns.sql b/ganttproject/src/main/resources/resources/sql/update-builtin-calculated-columns.sql new file mode 100644 index 0000000000..d57a692bd6 --- /dev/null +++ b/ganttproject/src/main/resources/resources/sql/update-builtin-calculated-columns.sql @@ -0,0 +1 @@ +UPDATE Task SET end_date=TASK_END_DATE(num), cost=TASK_COST(num); \ No newline at end of file diff --git a/ganttproject/src/test/java/biz/ganttproject/storage/SqlCustomPropertyStorageManagerTest.kt b/ganttproject/src/test/java/biz/ganttproject/storage/SqlCustomPropertyStorageManagerTest.kt index 115009a626..dc762947b9 100644 --- a/ganttproject/src/test/java/biz/ganttproject/storage/SqlCustomPropertyStorageManagerTest.kt +++ b/ganttproject/src/test/java/biz/ganttproject/storage/SqlCustomPropertyStorageManagerTest.kt @@ -18,13 +18,12 @@ */ package biz.ganttproject.storage -import biz.ganttproject.customproperty.CustomPropertyClass -import biz.ganttproject.customproperty.CustomPropertyHolder -import biz.ganttproject.customproperty.CustomPropertyManager -import biz.ganttproject.customproperty.SimpleSelect +import biz.ganttproject.customproperty.* import biz.ganttproject.storage.db.tables.Task import net.sourceforge.ganttproject.TestSetupHelper +import net.sourceforge.ganttproject.resource.HumanResourceManager import net.sourceforge.ganttproject.storage.* +import net.sourceforge.ganttproject.task.CostStub import net.sourceforge.ganttproject.task.TaskManager import org.h2.jdbcx.JdbcDataSource import org.jooq.SQLDialect @@ -34,6 +33,8 @@ import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows +import org.w3c.util.DateParser +import java.math.BigDecimal import java.sql.SQLException import java.util.* import javax.sql.DataSource @@ -82,7 +83,7 @@ class CalculatedPropertyTest { @Test fun `calculated property value`() { val foo = customPropertyManager.createDefinition(CustomPropertyClass.TEXT, "foo") - val bar =customPropertyManager.createDefinition(CustomPropertyClass.INTEGER, "bar").also { + val bar = customPropertyManager.createDefinition(CustomPropertyClass.INTEGER, "bar").also { it.calculationMethod = SimpleSelect(it.id, "duration + 1", resultClass = CustomPropertyClass.INTEGER.javaClass) } val baz = customPropertyManager.createDefinition(CustomPropertyClass.TEXT, "baz").also { @@ -99,17 +100,50 @@ class CalculatedPropertyTest { assertEquals(1, tasks.size) val propertyHolders = createPropertyHolders(taskManager) - val updaters = customPropertyManager.definitions.map {def -> - ColumnConsumer(SimpleSelect(def.id, def.id, resultClass = def.type)) { taskNum, value -> - propertyHolders[taskNum]?.setValue(def, value) - } - }.toList() - projectDatabase.mapTasks(*(updaters.toTypedArray())) + val updater = CalculatedPropertyUpdater(projectDatabase, {customPropertyManager}, {propertyHolders}) + updater.update() assertEquals(2, task.customValues.getValue(bar)) assertEquals("foo--", task.customValues.getValue(baz)) } + @Test + fun `builtin calculated property value`() { + val task = taskManager.newTaskBuilder().withName("task1").withStartDate(Date()).build() + projectDatabase.insertTask(task) + + H2Functions.taskManager.set(taskManager) + task.createMutator().let { + it.setCost(CostStub(BigDecimal.ZERO, true)) + it.setDuration(taskManager.createLength(2)) + it.commit() + } + + val updater = CalculatedPropertyUpdater(projectDatabase, {customPropertyManager}, { emptyMap() }) + updater.update() + + DSL.using(dataSource, SQLDialect.H2).also { + val tasks = it.selectFrom(Task.TASK).fetch() + assertEquals(1, tasks.size) + assertEquals(task.end.time, DateParser.toJavaDate(tasks[0].endDate)) + } + + val humanResourceManager = HumanResourceManager(null, CustomColumnsManager()) + val resource = humanResourceManager.newResourceBuilder().withName("foo").withID(1).withStandardRate(BigDecimal.valueOf(100)).build() + task.assignmentCollection.addAssignment(resource).also { + it.load = 100f; + } + + updater.update() + DSL.using(dataSource, SQLDialect.H2).also { + val tasks = it.selectFrom(Task.TASK).fetch() + assertEquals(1, tasks.size) + assertEquals(task.cost.value.toDouble(), tasks[0].cost.toDouble()) + assertEquals(200.0, tasks[0].cost.toDouble()) + } + + } + @Test fun `column used in a generated column can't be dropped`() { customPropertyManager.createDefinition(CustomPropertyClass.INTEGER, "bar").also {