diff --git a/cmd/server/pactasrv/conv/oapi_to_pacta.go b/cmd/server/pactasrv/conv/oapi_to_pacta.go
index ae6b1b5..193be02 100644
--- a/cmd/server/pactasrv/conv/oapi_to_pacta.go
+++ b/cmd/server/pactasrv/conv/oapi_to_pacta.go
@@ -103,12 +103,12 @@ func AnalysisTypeFromOAPI(at api.AnalysisType) (pacta.AnalysisType, error) {
return "", oapierr.BadRequest("analysisTypeFromOAPI: unknown analysis type", zap.String("analysis_type", string(at)))
}
-func HoldingsDateFromOAPI(hd *api.HoldingsDate) (*pacta.HoldingsDate, error) {
- if hd == nil {
+func HoldingsDateFromOAPI(hd api.HoldingsDate) (*pacta.HoldingsDate, error) {
+ if hd.Time == nil {
return nil, nil
}
return &pacta.HoldingsDate{
- Time: hd.Time,
+ Time: *hd.Time,
}, nil
}
diff --git a/cmd/server/pactasrv/conv/pacta_to_oapi.go b/cmd/server/pactasrv/conv/pacta_to_oapi.go
index 5c1f32a..7a93c88 100644
--- a/cmd/server/pactasrv/conv/pacta_to_oapi.go
+++ b/cmd/server/pactasrv/conv/pacta_to_oapi.go
@@ -177,12 +177,12 @@ func InitiativeUserRelationshipToOAPI(i *pacta.InitiativeUserRelationship) (*api
}, nil
}
-func HoldingsDateToOAPI(hd *pacta.HoldingsDate) (*api.HoldingsDate, error) {
+func HoldingsDateToOAPI(hd *pacta.HoldingsDate) (api.HoldingsDate, error) {
if hd == nil {
- return nil, nil
+ return api.HoldingsDate{}, nil
}
- return &api.HoldingsDate{
- Time: hd.Time,
+ return api.HoldingsDate{
+ Time: &hd.Time,
}, nil
}
@@ -208,12 +208,9 @@ func IncompleteUploadToOAPI(iu *pacta.IncompleteUpload) (*api.IncompleteUpload,
if err != nil {
return nil, oapierr.Internal("incompleteUploadToOAPI: failureCodeToOAPI failed", zap.Error(err))
}
- var hd *api.HoldingsDate
- if iu.Properties.HoldingsDate != nil {
- hd, err = HoldingsDateToOAPI(iu.Properties.HoldingsDate)
- if err != nil {
- return nil, oapierr.Internal("incompleteUploadToOAPI: holdingsDateToOAPI failed", zap.Error(err))
- }
+ hd, err := HoldingsDateToOAPI(iu.Properties.HoldingsDate)
+ if err != nil {
+ return nil, oapierr.Internal("incompleteUploadToOAPI: holdingsDateToOAPI failed", zap.Error(err))
}
return &api.IncompleteUpload{
Id: string(iu.ID),
@@ -255,12 +252,9 @@ func PortfolioToOAPI(p *pacta.Portfolio) (*api.Portfolio, error) {
if err != nil {
return nil, oapierr.Internal("initiativeToOAPI: portfolioInitiativeMembershipToOAPIInitiative failed", zap.Error(err))
}
- var hd *api.HoldingsDate
- if p.Properties.HoldingsDate != nil {
- hd, err = HoldingsDateToOAPI(p.Properties.HoldingsDate)
- if err != nil {
- return nil, oapierr.Internal("portfolioToOAPI: holdingsDateToOAPI failed", zap.Error(err))
- }
+ hd, err := HoldingsDateToOAPI(p.Properties.HoldingsDate)
+ if err != nil {
+ return nil, oapierr.Internal("portfolioToOAPI: holdingsDateToOAPI failed", zap.Error(err))
}
return &api.Portfolio{
Id: string(p.ID),
diff --git a/cmd/server/pactasrv/portfolio.go b/cmd/server/pactasrv/portfolio.go
index 41adde6..594acbd 100644
--- a/cmd/server/pactasrv/portfolio.go
+++ b/cmd/server/pactasrv/portfolio.go
@@ -91,7 +91,7 @@ func (s *Server) UpdatePortfolio(ctx context.Context, request api.UpdatePortfoli
mutations = append(mutations, db.SetPortfolioDescription(*request.Body.Description))
}
if request.Body.PropertyHoldingsDate != nil {
- hd, err := conv.HoldingsDateFromOAPI(request.Body.PropertyHoldingsDate)
+ hd, err := conv.HoldingsDateFromOAPI(*request.Body.PropertyHoldingsDate)
if err != nil {
return nil, err
}
diff --git a/cmd/server/pactasrv/upload.go b/cmd/server/pactasrv/upload.go
index 2f4a59a..08ee393 100644
--- a/cmd/server/pactasrv/upload.go
+++ b/cmd/server/pactasrv/upload.go
@@ -26,11 +26,9 @@ func (s *Server) StartPortfolioUpload(ctx context.Context, request api.StartPort
}
owner := &pacta.Owner{ID: actorInfo.OwnerID}
properties := pacta.PortfolioProperties{}
- if request.Body.PropertyHoldingsDate != nil {
- properties.HoldingsDate, err = conv.HoldingsDateFromOAPI(request.Body.PropertyHoldingsDate)
- if err != nil {
- return nil, err
- }
+ properties.HoldingsDate, err = conv.HoldingsDateFromOAPI(request.Body.PropertyHoldingsDate)
+ if err != nil {
+ return nil, err
}
properties.ESG = conv.OptionalBoolFromOAPI(request.Body.PropertyESG)
properties.External = conv.OptionalBoolFromOAPI(request.Body.PropertyExternal)
diff --git a/frontend/components/analysis/ContextualListView.vue b/frontend/components/analysis/ContextualListView.vue
index 37351c0..755c3e4 100644
--- a/frontend/components/analysis/ContextualListView.vue
+++ b/frontend/components/analysis/ContextualListView.vue
@@ -127,6 +127,7 @@ const reportButtonClasses = computed(() => {
:portfolio-id="props.portfolioId"
:portfolio-group-id="props.portfolioGroupId"
:initiative-id="props.initiativeId"
+ :warn-for-duplicate="hasAnyAudits"
class="p-button-sm"
@started="() => emit('refresh')"
/>
@@ -137,6 +138,7 @@ const reportButtonClasses = computed(() => {
:portfolio-id="props.portfolioId"
:portfolio-group-id="props.portfolioGroupId"
:initiative-id="props.initiativeId"
+ :warn-for-duplicate="hasAnyReports"
class="p-button-sm"
@started="() => emit('refresh')"
/>
diff --git a/frontend/components/analysis/RunButton.vue b/frontend/components/analysis/RunButton.vue
index 384fb9b..fefd659 100644
--- a/frontend/components/analysis/RunButton.vue
+++ b/frontend/components/analysis/RunButton.vue
@@ -1,6 +1,8 @@
diff --git a/frontend/components/modal/Group.vue b/frontend/components/modal/Group.vue
index ebee878..d1e1f94 100644
--- a/frontend/components/modal/Group.vue
+++ b/frontend/components/modal/Group.vue
@@ -9,5 +9,9 @@
+
diff --git a/frontend/lang/en.json b/frontend/lang/en.json
index 477d065..f6c2586 100644
--- a/frontend/lang/en.json
+++ b/frontend/lang/en.json
@@ -220,7 +220,11 @@
"components/analysis/RunButton": {
"Run": "Run",
"AnalysisTypeAUDIT": "Audit",
- "AnalysisTypeREPORT": "Report"
+ "AnalysisTypeREPORT": "Report",
+ "ConfirmationHeader": "Run Duplicate Analysis?",
+ "ConfirmationMessage": "It looks like there is already a run for this analysis - if you have changed the settings or underlying data for this analysis, a new run may be warranted, but otherwise this will just return the same analysis as above.",
+ "Run Anyway": "Run Anyway",
+ "Cancel Run": "Nevermind"
},
"components/portfolio/group/ListView": {
"Created At": "Created At",
diff --git a/frontend/openapi/generated/pacta/models/HoldingsDate.ts b/frontend/openapi/generated/pacta/models/HoldingsDate.ts
index 11c37ae..37c9d35 100644
--- a/frontend/openapi/generated/pacta/models/HoldingsDate.ts
+++ b/frontend/openapi/generated/pacta/models/HoldingsDate.ts
@@ -7,6 +7,6 @@ export type HoldingsDate = {
/**
* The time at which the holdings are represented at
*/
- time: string;
+ time?: string;
};
diff --git a/frontend/openapi/generated/pacta/models/IncompleteUpload.ts b/frontend/openapi/generated/pacta/models/IncompleteUpload.ts
index d02ba35..9e5146c 100644
--- a/frontend/openapi/generated/pacta/models/IncompleteUpload.ts
+++ b/frontend/openapi/generated/pacta/models/IncompleteUpload.ts
@@ -20,7 +20,7 @@ export type IncompleteUpload = {
* Description of the upload
*/
description: string;
- propertyHoldingsDate?: HoldingsDate;
+ propertyHoldingsDate: HoldingsDate;
/**
* If set, this portfolio represents ESG data
*/
diff --git a/frontend/openapi/generated/pacta/models/Portfolio.ts b/frontend/openapi/generated/pacta/models/Portfolio.ts
index 729d843..2240c63 100644
--- a/frontend/openapi/generated/pacta/models/Portfolio.ts
+++ b/frontend/openapi/generated/pacta/models/Portfolio.ts
@@ -41,7 +41,7 @@ export type Portfolio = {
* The list of initiatives that this portfolio is a member of
*/
initiatives?: Array;
- propertyHoldingsDate?: HoldingsDate;
+ propertyHoldingsDate: HoldingsDate;
/**
* If set, this portfolio represents ESG data
*/
diff --git a/frontend/openapi/generated/pacta/models/StartPortfolioUploadReq.ts b/frontend/openapi/generated/pacta/models/StartPortfolioUploadReq.ts
index ce128a1..8489ed3 100644
--- a/frontend/openapi/generated/pacta/models/StartPortfolioUploadReq.ts
+++ b/frontend/openapi/generated/pacta/models/StartPortfolioUploadReq.ts
@@ -9,7 +9,10 @@ import type { StartPortfolioUploadReqItem } from './StartPortfolioUploadReqItem'
export type StartPortfolioUploadReq = {
items: Array;
- propertyHoldingsDate?: HoldingsDate;
+ /**
+ * The holdings date of the portfolio, if set
+ */
+ propertyHoldingsDate: HoldingsDate;
/**
* If set, this portfolio represents ESG data
*/
diff --git a/frontend/pages/upload.vue b/frontend/pages/upload.vue
index 07b3d49..5a73cf0 100644
--- a/frontend/pages/upload.vue
+++ b/frontend/pages/upload.vue
@@ -39,7 +39,7 @@ interface FileStateDetail extends FileState {
effectiveError?: string | undefined
}
-const holdingsDate = useState(`${prefix}.holdingsDate`, () => undefined)
+const holdingsDate = useState(`${prefix}.holdingsDate`, () => ({ time: undefined }))
const esg = useState(`${prefix}.esg`, () => OptionalBoolean.OPTIONAL_BOOLEAN_UNSET)
const external = useState(`${prefix}.external`, () => OptionalBoolean.OPTIONAL_BOOLEAN_UNSET)
const engagementStrategy = useState(`${prefix}.engagementStrategy`, () => OptionalBoolean.OPTIONAL_BOOLEAN_UNSET)
@@ -50,7 +50,7 @@ const isProcessing = useState(`${prefix}.isProcessing`, () => false)
const fileStates = useState(`${prefix}.fileState`, () => [])
const reset = () => {
- holdingsDate.value = undefined
+ holdingsDate.value = { time: undefined }
esg.value = OptionalBoolean.OPTIONAL_BOOLEAN_UNSET
external.value = OptionalBoolean.OPTIONAL_BOOLEAN_UNSET
engagementStrategy.value = OptionalBoolean.OPTIONAL_BOOLEAN_UNSET
diff --git a/frontend/plugins/primevue.ts b/frontend/plugins/primevue.ts
index 1034938..e80e3ee 100644
--- a/frontend/plugins/primevue.ts
+++ b/frontend/plugins/primevue.ts
@@ -9,6 +9,8 @@ import Calendar from 'primevue/calendar'
import Card from 'primevue/card'
import Chips from 'primevue/chips'
import Column from 'primevue/column'
+import ConfirmDialog from 'primevue/confirmdialog'
+import ConfirmationService from 'primevue/confirmationservice'
import DataTable from 'primevue/datatable'
import Dialog from 'primevue/dialog'
import Dropdown from 'primevue/dropdown'
@@ -42,6 +44,7 @@ export default defineNuxtPlugin(({ vueApp }) => {
vueApp.component('PVCard', Card)
vueApp.component('PVChips', Chips)
vueApp.component('PVColumn', Column)
+ vueApp.component('PVConfirmDialog', ConfirmDialog)
vueApp.component('PVDataTable', DataTable)
vueApp.component('PVDialog', Dialog)
vueApp.component('PVDropdown', Dropdown)
@@ -62,4 +65,5 @@ export default defineNuxtPlugin(({ vueApp }) => {
vueApp.component('PVTriStateCheckbox', TriStateCheckbox)
vueApp.directive('tooltip', Tooltip)
+ vueApp.use(ConfirmationService)
})
diff --git a/openapi/pacta.yaml b/openapi/pacta.yaml
index a1d8501..a703956 100644
--- a/openapi/pacta.yaml
+++ b/openapi/pacta.yaml
@@ -1508,6 +1508,7 @@ components:
type: object
required:
- items
+ - propertyHoldingsDate
- propertyESG
- propertyExternal
- propertyEngagementStrategy
@@ -1517,6 +1518,7 @@ components:
items:
$ref: '#/components/schemas/StartPortfolioUploadReqItem'
propertyHoldingsDate:
+ description: The holdings date of the portfolio, if set
$ref: '#/components/schemas/HoldingsDate'
propertyESG:
description: If set, this portfolio represents ESG data
@@ -1626,8 +1628,6 @@ components:
description: The time at which the signed URL will expire.
HoldingsDate:
type: object
- required:
- - time
properties:
time:
type: string
@@ -1641,6 +1641,7 @@ components:
- description
- createdAt
- adminDebugEnabled
+ - propertyHoldingsDate
- propertyESG
- propertyExternal
- propertyEngagementStrategy
@@ -1708,6 +1709,7 @@ components:
- createdAt
- adminDebugEnabled
- numberOfRows
+ - propertyHoldingsDate
- propertyESG
- propertyExternal
- propertyEngagementStrategy