diff --git a/grails-app/controllers/au/org/ala/merit/AdminController.groovy b/grails-app/controllers/au/org/ala/merit/AdminController.groovy
index a8d54a126..f1c5f23ab 100644
--- a/grails-app/controllers/au/org/ala/merit/AdminController.groovy
+++ b/grails-app/controllers/au/org/ala/merit/AdminController.groovy
@@ -418,7 +418,7 @@ class AdminController {
session.status = status
def fileIn = new FileInputStream(file)
try {
- def result = importService.gmsImport(fileIn, status.projects, preview, update)
+ def result = importService.projectImport(fileIn, status.projects, preview, update)
status.finished = true
status.error = result.error
}
diff --git a/grails-app/services/au/org/ala/merit/ImportService.groovy b/grails-app/services/au/org/ala/merit/ImportService.groovy
index f8b3cc60c..ca9e4a0fb 100644
--- a/grails-app/services/au/org/ala/merit/ImportService.groovy
+++ b/grails-app/services/au/org/ala/merit/ImportService.groovy
@@ -297,7 +297,7 @@ class ImportService {
organisations += metadataService.organisationList()?.list
}
- Map gmsImport(InputStream csv, List status, Boolean preview, Boolean update, String charEncoding = 'Cp1252') {
+ Map projectImport(InputStream csv, List status, Boolean preview, Boolean update, String charEncoding = 'Cp1252') {
Map programs = [:].withDefault{name ->
programService.getByName(name)
@@ -307,8 +307,7 @@ class ImportService {
mu?.managementUnitId
}
refreshOrganisationList()
- def mapper = new GmsMapper(metadataService.activitiesModel(), metadataService.programsModel(), organisations, abnLookupService, metadataService.getOutputTargetScores(), programs, managementUnits)
-
+ def mapper = new GmsMapper(metadataService.activitiesModel(), metadataService.programsModel(), organisations, abnLookupService, metadataService.getOutputTargetScores(), programs, managementUnits, false, update)
def action = preview?{rows -> mapProjectRows(rows, status, mapper, update)}:{rows -> importAll(rows, status, mapper, update)}
Map result = [:]
@@ -360,7 +359,7 @@ class ImportService {
def mapProjectRows(projectRows, List status, GmsMapper mapper, Boolean update) {
- Map mappingResults = mapper.mapProject(projectRows)
+ Map mappingResults = mapper.mapProject(projectRows, update)
String grantId = mappingResults.project.grantId
String externalId = mappingResults.project.externalId
@@ -380,7 +379,7 @@ class ImportService {
void importAll(projectRows, List status, GmsMapper mapper, Boolean update) {
- def projectDetails = mapper.mapProject(projectRows)
+ def projectDetails = mapper.mapProject(projectRows, update)
def grantId = projectDetails.project.grantId?:''
def externalId = projectDetails.project.externalId?:''
@@ -394,9 +393,6 @@ class ImportService {
def editorEmail = projectDetails.project.remove('editorEmail')
def editorEmail2 = projectDetails.project.remove('editorEmail2')
- //When projects are loaded into MERIT via CSV upload, they are given a status of "Application".
- projectDetails.project.status ?: 'application'
-
// Create the organisation first so we can link it to the project.
if (projectDetails.organisation) {
Map orgCreationResult = organisationService.update(null, projectDetails.organisation)
@@ -409,7 +405,7 @@ class ImportService {
}
}
- def result = importProject(projectDetails.project, update) // Do not overwrite existing projects because of the impacts to sites / activities etc.
+ def result = importProject(projectDetails.project, update)
if (result.project == 'existing' && !update) {
status << [grantId:grantId, externalId:externalId, success:false, errors:['Project already exists in MERIT, skipping']]
diff --git a/grails-app/views/admin/import.gsp b/grails-app/views/admin/import.gsp
index fad01a314..5df7ef77e 100644
--- a/grails-app/views/admin/import.gsp
+++ b/grails-app/views/admin/import.gsp
@@ -34,9 +34,7 @@
- Please note this function will replace all project information for each project.
- It is designed for use with grants hub data and to fix newly loaded projects. Please test changes
- to existing MERIT projects in the staging system before running the update in production.
+ Please test changes to existing MERIT projects in the staging system before running the update in production.
diff --git a/src/integration-test/groovy/au/org/ala/fieldcapture/ImportProjectsSpec.groovy b/src/integration-test/groovy/au/org/ala/fieldcapture/ImportProjectsSpec.groovy
index f83d2a156..12fef4bc9 100644
--- a/src/integration-test/groovy/au/org/ala/fieldcapture/ImportProjectsSpec.groovy
+++ b/src/integration-test/groovy/au/org/ala/fieldcapture/ImportProjectsSpec.groovy
@@ -49,6 +49,7 @@ class ImportProjectsSpec extends StubbedCasSpec {
and:
List rows2 = projectResults()
rows2.size() == 2
+ rows2[1].success == 'Yes'
when:
to Organisation, 'test_organisation'
@@ -75,6 +76,8 @@ class ImportProjectsSpec extends StubbedCasSpec {
and: "The data is relevant to the projects loaded"
List rows = projectResults()
rows.size() == 3
+ rows[1].success == 'Yes'
+ rows[2].success == 'Yes'
when:
importProjects()
@@ -125,6 +128,7 @@ class ImportProjectsSpec extends StubbedCasSpec {
and:
List rows2 = projectResults()
rows2.size() == 2
+ rows2[1].success == 'Yes'
when: "We navigate to the program page to find the new imported project, then open it"
to ProgramPage, 'configurable_meri_plan'
@@ -152,4 +156,59 @@ class ImportProjectsSpec extends StubbedCasSpec {
adminContent.meriPlan.budget[0].budgetAmounts()*.value() == ["20000", "10000"]
}
+
+ def "Projects can be updated via import with only the fields provided in the spreadsheet being updated"() {
+
+ setup:
+ File csv = new File(getClass().getResource("/grants-hub-update-data.csv").toURI())
+ loginAsMeritAdmin(browser)
+
+ when:
+ to ProjectImport
+ checkUpdateCheckbox()
+ attachFile(csv)
+
+ then: "The projects are validated and the validation results are displayed"
+ waitFor{validateComplete()}
+
+ and: "The data is relevant to the projects loaded"
+ projectResults().size() == 2
+
+ when:
+ importProjects()
+
+ then:
+ waitFor{loadComplete()}
+ and:
+ List rows2 = projectResults()
+ rows2.size() == 2
+ rows2[1].success == 'Yes'
+
+ when: "We navigate to the program page to find the new imported project, then open it"
+ to ProgramPage, 'configurable_meri_plan'
+ openProjectByGrantId('cep-1')
+
+ then:
+ at RlpProjectPage
+
+ when:
+ displayOverview()
+
+ then:
+ overview.program.text() == "Configurable MERI Plan Program"
+ overview.projectId.text() == "cep-1"
+ overview.status.text().equalsIgnoreCase("Active")
+ overview.externalIds*.text() == ["1234"]
+ overview.description.text() == "Grants project description - updated"
+
+ when:
+ openMeriPlanEditTab()
+
+ then:
+ adminContent.meriPlan.budget.size() == 1
+ adminContent.meriPlan.budget[0].description.value() == "Project funding"
+ adminContent.meriPlan.budget[0].budgetAmounts()*.value() == ["1", "2"]
+
+ }
+
}
diff --git a/src/integration-test/groovy/pages/ProjectImport.groovy b/src/integration-test/groovy/pages/ProjectImport.groovy
index 67aa11223..ab7cde215 100644
--- a/src/integration-test/groovy/pages/ProjectImport.groovy
+++ b/src/integration-test/groovy/pages/ProjectImport.groovy
@@ -1,6 +1,7 @@
package pages
import geb.Page
+import geb.module.Checkbox
class ProjectImport extends Page {
@@ -12,6 +13,7 @@ class ProjectImport extends Page {
fileInput { $('#fileUpload') }
importButton { $('button[data-bind*=doImport]')}
progressSummary { $('span[data-bind*=progressSummary]') }
+ updateCheckbox { $('#update').module(Checkbox) }
}
def attachFile(File file) {
@@ -22,7 +24,13 @@ class ProjectImport extends Page {
importButton.click()
}
+ def checkUpdateCheckbox() {
+ updateCheckbox.check()
+ }
+
List projectResults() {
+
+ List columns = ["grantId", "externalId", "success", "errors", "messages"]
List rows = []
def progressTable = $('table.table')
progressTable.find("tbody tr").each {
@@ -30,7 +38,11 @@ class ProjectImport extends Page {
it.find("td").each { col ->
cols << col.text()
}
- rows << cols
+ Map row = [:]
+ columns.eachWithIndex{ col, index ->
+ row[col] = cols[index]
+ }
+ rows << row
}
rows
diff --git a/src/integration-test/resources/grants-hub-update-data.csv b/src/integration-test/resources/grants-hub-update-data.csv
new file mode 100644
index 000000000..66c5c3b95
--- /dev/null
+++ b/src/integration-test/resources/grants-hub-update-data.csv
@@ -0,0 +1,2 @@
+APP_ID,EXTERNAL_ID,APP_NM,APP_DESC,PROGRAM_NM,ABN,ORG_ID,START_DT,FINISH_DT,ORDER_NO,FUNDING,AUTHORISEDP_EMAIL,GRANT_MGR_EMAIL,GRANT_MGR_EMAIL_2,APPLICANT_EMAIL,ADMIN_EMAIL,EDITOR_EMAIL,EDITOR_EMAIL_2,TAGS,PROJECT_STATUS,MERI_PLAN_STATUS,FUNDING_TYPE,ORIGIN_SYSTEM,FINANCIAL_YEAR_FUNDING_DESCRIPTION,FUNDING_21_22,FUNDING_22_23
+cep-1,,,Grants project description - updated,,,,,,,,,,,,,,,,Active,,,,Project funding,1,2
\ No newline at end of file
diff --git a/src/main/groovy/au/org/ala/merit/GmsMapper.groovy b/src/main/groovy/au/org/ala/merit/GmsMapper.groovy
index 1e58bd763..b3580394b 100644
--- a/src/main/groovy/au/org/ala/merit/GmsMapper.groovy
+++ b/src/main/groovy/au/org/ala/merit/GmsMapper.groovy
@@ -2,7 +2,7 @@ package au.org.ala.merit
import au.com.bytecode.opencsv.CSVWriter
import org.apache.commons.lang.WordUtils
-import org.apache.commons.validator.EmailValidator
+import org.apache.commons.validator.routines.EmailValidator
import java.text.DecimalFormat
import java.text.SimpleDateFormat
@@ -95,6 +95,7 @@ class GmsMapper {
def geographicInfoMapping = [
NATIONWIDE:[name:'nationwide', type:'boolean', description:'If true, this project does not have a primary state'],
+ STATEWIDE:[name:'statewide', type:'boolean', description:'If true, this project does not have a primary electorate'],
PRIMARY_STATE:[name:'primaryState', type:'string', description:'The primary state to be manually assigned to this project'],
PRIMARY_ELECTORATE:[name:'primaryElectorate', type:'string', description:'The primary electorate to be manually assigned to this project'],
OTHER_ELECTORATES:[name:'otherElectorates', type:'list', description:'Other electorates to be manually assigned to this project. Enter as a comma separated list'],
@@ -135,6 +136,8 @@ class GmsMapper {
]
private boolean includeProgress
+ /** If the mapping is being done to update a project, mandatory fields aren't required as they won't be updated if absent */
+ private boolean mapForUpdate = false
public GmsMapper() {
this.activitiesModel = []
@@ -144,9 +147,10 @@ class GmsMapper {
this.programs = [:]
this.managementUnits = [:]
includeProgress = false
+ mapForUpdate = false
}
- GmsMapper(activitiesModel, programModel, organisations, abnLookup, List