From b4b1e13fd1afdd81bfab18e7c3d77f28d782b2e6 Mon Sep 17 00:00:00 2001 From: MacRae Linton <55759+macrael@users.noreply.github.com> Date: Tue, 25 Jun 2019 10:07:42 -0700 Subject: [PATCH 1/6] Move from acccount.Locked to account.Status --- api/account.go | 67 +++++++++++++++---- api/admin/reject.go | 15 ++++- api/admin/submit.go | 5 -- api/cmd/unlock/main.go | 5 +- api/http/attachment.go | 6 +- api/http/form.go | 2 +- api/http/save.go | 2 +- api/http/status.go | 4 +- api/http/submit.go | 11 ++- api/integration/application_status_test.go | 7 +- api/integration/clear_yes_no_test.go | 24 +++++++ api/integration/helpers.go | 3 +- .../20190623111111_create_form_status.sql | 18 +++++ api/postgresql/db_test.go | 1 + api/simplestore/applications_test.go | 4 +- src/actions/ApplicationActions.js | 4 +- .../Section/Package/Submit/index.jsx | 4 +- src/schema/section/submission.js | 3 +- src/selectors/navigation.js | 15 ++--- src/validators/releases.js | 2 +- 20 files changed, 148 insertions(+), 54 deletions(-) create mode 100644 api/migrations/20190623111111_create_form_status.sql diff --git a/api/account.go b/api/account.go index 1f4b76373..cc0c64d40 100644 --- a/api/account.go +++ b/api/account.go @@ -15,6 +15,9 @@ var ( // ErrDatastoreConnection is an error when a database connection cannot be made ErrDatastoreConnection = errors.New("Unable to connect to datastore") + + // ErrInvalidStatusTransition is an error when a database connection cannot be made + ErrInvalidStatusTransition = errors.New("Invalid Status Transition") ) // knownFormVersions is a map of FormType -> slice of known versions sorted with most recent first. @@ -29,6 +32,12 @@ var knownFormVersions = map[string][]string{ }, } +const ( + StatusIncomplete = "INCOMPLETE" + StatusSubmitted = "SUBMITTED" + StatusKickback = "KICKBACK" +) + // Account represents a user account type Account struct { ID int @@ -38,7 +47,7 @@ type Account struct { Token string TokenUsed bool Email string - Locked bool + Status string FormType string `db:"form_type"` FormVersion string `db:"form_version"` ExternalID string `db:"external_id"` // ExternalID is an identifier used by external systems to id applications @@ -124,18 +133,52 @@ func (entity *Account) Find(context DatabaseService) error { return context.Select(entity) } -// Lock will mark the account in a `locked` status. -func (entity *Account) Lock(context DatabaseService) error { - entity.Locked = true - _, err := entity.Save(context, entity.ID) - return err +// FindByExternalID will search for account by `request id` +func (entity *Account) FindByExternalID(context DatabaseService) error { + if entity.ExternalID == "" { + return fmt.Errorf("No request id was given") + } + + return context.Where(entity, "Account.external_id = ?", entity.ExternalID) +} + +func (entity *Account) CanSubmit() bool { + if entity.Status == StatusSubmitted { + return false + } + return true } -// Unlock will mark the account in an `unlocked` status. -func (entity *Account) Unlock(context DatabaseService) error { - entity.Locked = false - _, err := entity.Save(context, entity.ID) - return err +// Submit will mark the account in a `SUBMITTED` status. +func (entity *Account) Submit() bool { + if !entity.CanSubmit() { + return false + } + + entity.Status = StatusSubmitted + return true +} + +// Unsubmit will mark the account in a `INCOMPLETE` status. This will likely be for debugging purposes only. +func (entity *Account) Unsubmit() { + entity.Status = StatusIncomplete +} + +func (entity *Account) CanKickback() bool { + if entity.Status != StatusSubmitted { + return false + } + return true +} + +// Kickback will mark the account in a `KICKBACK` status. +func (entity *Account) Kickback() bool { + if !entity.CanKickback() { + return false + } + + entity.Status = StatusKickback + return true } // FormTypeIsKnown returns wether the form type and version are known to eApp @@ -173,7 +216,7 @@ func (entity *Account) BasicAuthentication(context DatabaseService, password str entity.Token = basicMembership.Account.Token entity.TokenUsed = basicMembership.Account.TokenUsed entity.Email = basicMembership.Account.Email - entity.Locked = basicMembership.Account.Locked + entity.Status = basicMembership.Account.Status entity.FormType = basicMembership.Account.FormType entity.FormVersion = basicMembership.Account.FormVersion } diff --git a/api/admin/reject.go b/api/admin/reject.go index 37060883c..b561e3051 100644 --- a/api/admin/reject.go +++ b/api/admin/reject.go @@ -24,9 +24,8 @@ func NewRejecter(db api.DatabaseService, store api.StorageService, pdf api.PdfSe // Reject rejects the application for a given account func (r Rejecter) Reject(account api.Account) error { - err := account.Unlock(r.db) - if err != nil { - return errors.Wrap(err, "Reject failed to unlock account") + if !account.CanKickback() { + return errors.New("Account can't be rejected if it hasn't been submitted") } // TODO: port over PDF.RemovePdfs. @@ -53,5 +52,15 @@ func (r Rejecter) Reject(account api.Account) error { return errors.Wrap(saveErr, "Unable to save application after rejecting it") } + ok := account.Kickback() + if !ok { + return errors.New("The account got into a bad state during the rejection.") + } + + _, saveAccErr := account.Save(r.db, account.ID) + if saveAccErr != nil { + return errors.Wrap(saveAccErr, "couldn't save the account after changing the status") + } + return nil } diff --git a/api/admin/submit.go b/api/admin/submit.go index ab1b79031..0dc24cf0d 100644 --- a/api/admin/submit.go +++ b/api/admin/submit.go @@ -27,7 +27,6 @@ func NewSubmitter(db api.DatabaseService, store api.StorageService, xml api.XMLS // FilesForSubmission returns the XML and any Attachments for submission. func (s Submitter) FilesForSubmission(accountID int) ([]byte, []api.Attachment, error) { - // check that the acount isn't locked // Get the account information from the data store account := api.Account{ID: accountID} _, accountErr := account.Get(s.db, accountID) @@ -35,10 +34,6 @@ func (s Submitter) FilesForSubmission(accountID int) ([]byte, []api.Attachment, return []byte{}, []api.Attachment{}, accountErr } - if account.Locked { - return []byte{}, []api.Attachment{}, errors.New("Account is locked") - } - application, appErr := s.store.LoadApplication(accountID) if appErr != nil { return []byte{}, []api.Attachment{}, errors.Wrap(appErr, "Can't load applicaiton") diff --git a/api/cmd/unlock/main.go b/api/cmd/unlock/main.go index e1377b95d..5b504460d 100644 --- a/api/cmd/unlock/main.go +++ b/api/cmd/unlock/main.go @@ -9,8 +9,9 @@ import ( func main() { logger := &log.Service{Log: log.NewLogger()} cmd.Command(logger, func(context api.DatabaseService, store api.StorageService, account *api.Account) { - if err := account.Unlock(context); err != nil { - logger.WarnError("Failed to unlock account", err, api.LogFields{"account": account.Username}) + account.Unsubmit() + if _, err := account.Save(context, account.ID); err != nil { + logger.WarnError("Failed to save unlocked account", err, api.LogFields{"account": account.Username}) } else { logger.Warn("Account unlocked", api.LogFields{"account": account.Username}) } diff --git a/api/http/attachment.go b/api/http/attachment.go index f2844d421..0c5560141 100644 --- a/api/http/attachment.go +++ b/api/http/attachment.go @@ -83,8 +83,8 @@ func (service AttachmentSaveHandler) ServeHTTP(w http.ResponseWriter, r *http.Re return } - // If the account is locked then we cannot proceed - if account.Locked { + // If the account is submitted then we cannot proceed + if account.Status == api.StatusSubmitted { service.Log.Warn(api.AccountLocked, api.LogFields{}) RespondWithStructuredError(w, api.AccountLocked, http.StatusForbidden) return @@ -239,7 +239,7 @@ func (service AttachmentDeleteHandler) ServeHTTP(w http.ResponseWriter, r *http. } // If the account is locked then we cannot proceed - if account.Locked { + if account.Status == api.StatusSubmitted { service.Log.Warn(api.AccountLocked, api.LogFields{}) RespondWithStructuredError(w, api.AccountLocked, http.StatusForbidden) return diff --git a/api/http/form.go b/api/http/form.go index e936370ac..9714f0ea8 100644 --- a/api/http/form.go +++ b/api/http/form.go @@ -33,7 +33,7 @@ func (service FormHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } // If the account is locked then we cannot proceed - if account.Locked { + if account.Status == api.StatusSubmitted { service.Log.Warn(api.AccountLocked, api.LogFields{}) RespondWithStructuredError(w, api.AccountLocked, http.StatusForbidden) return diff --git a/api/http/save.go b/api/http/save.go index fc49bea9f..e0a528ffa 100644 --- a/api/http/save.go +++ b/api/http/save.go @@ -31,7 +31,7 @@ func (service SaveHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } // If the account is locked then we cannot proceed - if account.Locked { + if account.Status == api.StatusSubmitted { service.Log.Warn(api.AccountLocked, api.LogFields{}) RespondWithStructuredError(w, api.AccountLocked, http.StatusForbidden) return diff --git a/api/http/status.go b/api/http/status.go index f2cfc7621..a6d896f07 100644 --- a/api/http/status.go +++ b/api/http/status.go @@ -20,7 +20,7 @@ type StatusHandler struct { // formStatusInfo represents extra information associated with the application // regarding its current state. type formStatusInfo struct { - Locked bool + Status string Hash string } @@ -57,7 +57,7 @@ func (service StatusHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } status := formStatusInfo{ - Locked: account.Locked, + Status: account.Status, Hash: hash, } diff --git a/api/http/submit.go b/api/http/submit.go index 6fef5976e..fefc2c503 100644 --- a/api/http/submit.go +++ b/api/http/submit.go @@ -33,7 +33,7 @@ func (service SubmitHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } // If the account is locked then we cannot proceed - if account.Locked { + if account.Status == api.StatusSubmitted { service.Log.Warn(api.AccountLocked, api.LogFields{}) RespondWithStructuredError(w, api.AccountLocked, http.StatusForbidden) return @@ -47,7 +47,14 @@ func (service SubmitHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } // Lock the account - if err := account.Lock(service.Database); err != nil { + ok := account.Submit() + if !ok { + service.Log.Warn(api.AccountUpdateError, api.LogFields{"status": account.Status}) + RespondWithStructuredError(w, api.AccountUpdateError, http.StatusInternalServerError) + return + } + + if _, err := account.Save(service.Database, account.ID); err != nil { service.Log.WarnError(api.AccountUpdateError, err, api.LogFields{}) RespondWithStructuredError(w, api.AccountUpdateError, http.StatusInternalServerError) return diff --git a/api/integration/application_status_test.go b/api/integration/application_status_test.go index b42edb2f6..fad95b22e 100644 --- a/api/integration/application_status_test.go +++ b/api/integration/application_status_test.go @@ -7,6 +7,7 @@ import ( "net/http/httptest" "testing" + "github.com/18F/e-QIP-prototype/api" "github.com/18F/e-QIP-prototype/api/http" ) @@ -151,7 +152,7 @@ func TestLockedStatus(t *testing.T) { } var status struct { - Locked bool + Status string Hash string } @@ -160,8 +161,8 @@ func TestLockedStatus(t *testing.T) { t.Fatal(jsonErr) } - if status.Locked != true { - t.Log("The account should not be locked") + if status.Status != api.StatusSubmitted { + t.Log("The account should be locked:", status.Status) t.Fail() } diff --git a/api/integration/clear_yes_no_test.go b/api/integration/clear_yes_no_test.go index 2a01626f0..452801ce0 100644 --- a/api/integration/clear_yes_no_test.go +++ b/api/integration/clear_yes_no_test.go @@ -21,6 +21,14 @@ func TestClearEmptyAccount(t *testing.T) { services := cleanTestServices(t) account := createTestAccount(t, services.db) + // Hacky, but seems OK for these tests. Technically you shouldn't be able to submit + // anything but a complete application, but I think it's ok to make these tests smaller. + account.Submit() + _, saveErr := account.Save(services.db, account.ID) + if saveErr != nil { + t.Fatal(saveErr) + } + rejector := admin.NewRejecter(services.db, services.store, nil) err := rejector.Reject(account) @@ -107,6 +115,14 @@ func rejectSection(t *testing.T, services serviceSet, json []byte, sectionName s t.Fatal("Failed to save JSON", resp.StatusCode) } + // Hacky, but seems OK for these tests. Technically you shouldn't be able to submit + // anything but a complete application, but I think it's ok to make these tests smaller. + account.Submit() + _, saveErr := account.Save(services.db, account.ID) + if saveErr != nil { + t.Fatal(saveErr) + } + rejector := admin.NewRejecter(services.db, services.store, nil) err := rejector.Reject(account) if err != nil { @@ -1036,6 +1052,14 @@ func TestClearComplexSectionNos(t *testing.T) { t.Fatal("Failed to save JSON", resp.StatusCode) } + // Hacky, but seems OK for these tests. Technically you shouldn't be able to submit + // anything but a complete application, but I think it's ok to make these tests smaller. + account.Submit() + _, saveErr := account.Save(services.db, account.ID) + if saveErr != nil { + t.Fatal(saveErr) + } + rejector := admin.NewRejecter(services.db, services.store, nil) err := rejector.Reject(account) if err != nil { diff --git a/api/integration/helpers.go b/api/integration/helpers.go index 391c22322..b34051955 100644 --- a/api/integration/helpers.go +++ b/api/integration/helpers.go @@ -101,7 +101,7 @@ func createLockedTestAccount(t *testing.T, db api.DatabaseService) api.Account { Email: email, FormType: "SF86", FormVersion: "2017-07", - Locked: true, + Status: api.StatusSubmitted, ExternalID: uuid.New().String(), } @@ -123,6 +123,7 @@ func createTestAccount(t *testing.T, db api.DatabaseService) api.Account { Email: email, FormType: "SF86", FormVersion: "2017-07", + Status: api.StatusIncomplete, ExternalID: uuid.New().String(), } diff --git a/api/migrations/20190623111111_create_form_status.sql b/api/migrations/20190623111111_create_form_status.sql new file mode 100644 index 000000000..ed1c13686 --- /dev/null +++ b/api/migrations/20190623111111_create_form_status.sql @@ -0,0 +1,18 @@ + +-- +goose Up +-- SQL in section 'Up' is executed when this migration is applied +-- +goose StatementBegin +ALTER TABLE accounts ADD COLUMN status text NOT NULL DEFAULT 'INCOMPLETE'; +UPDATE accounts SET status = 'SUBMITTED' WHERE locked = true; +ALTER TABLE accounts ALTER COLUMN status DROP DEFAULT; +ALTER TABLE accounts DROP COLUMN locked; +-- +goose StatementEnd + +-- +goose Down +-- SQL section 'Down' is executed when this migration is rolled back +-- +goose StatementBegin +ALTER TABLE accounts ADD COLUMN locked bool NOT NULL DEFAULT false; +UPDATE accounts SET locked = true WHERE status = 'SUBMITTED'; +ALTER TABLE accounts ALTER COLUMN locked DROP DEFAULT; +ALTER TABLE accounts DROP COLUMN status; +-- +goose StatementEnd diff --git a/api/postgresql/db_test.go b/api/postgresql/db_test.go index e178e3caa..81d43fef2 100644 --- a/api/postgresql/db_test.go +++ b/api/postgresql/db_test.go @@ -33,6 +33,7 @@ func TestAccountPersistence(t *testing.T) { Email: "buzz1@example.com", FormType: "SF86", FormVersion: "2017-07", + Status: api.StatusIncomplete, ExternalID: uuid.New().String(), } diff --git a/api/simplestore/applications_test.go b/api/simplestore/applications_test.go index 807f55e6f..923033be9 100644 --- a/api/simplestore/applications_test.go +++ b/api/simplestore/applications_test.go @@ -87,7 +87,7 @@ func areEqualJSON(t *testing.T, s1, s2 []byte) bool { func createAccount(t *testing.T, store SimpleStore) api.Account { t.Helper() - createQuery := `INSERT INTO accounts (username, email, form_type, form_version, external_id) VALUES ($1, $1, $2, $3, $4) RETURNING id, username, email, form_type, form_version, external_id` + createQuery := `INSERT INTO accounts (username, email, status, form_type, form_version, external_id) VALUES ($1, $1, $2, $3, $4, $5) RETURNING id, username, email, form_type, form_version, external_id` email := randomEmail() @@ -95,7 +95,7 @@ func createAccount(t *testing.T, store SimpleStore) api.Account { externalID := uuid.New().String() - createErr := store.db.Get(&result, createQuery, email, "SF86", "2017-07", externalID) + createErr := store.db.Get(&result, createQuery, email, api.StatusIncomplete, "SF86", "2017-07", externalID) if createErr != nil { t.Log("Failed to create Account", createErr) t.Fatal() diff --git a/src/actions/ApplicationActions.js b/src/actions/ApplicationActions.js index 5797db9ca..c921645a3 100644 --- a/src/actions/ApplicationActions.js +++ b/src/actions/ApplicationActions.js @@ -30,10 +30,10 @@ export function getApplicationState(done) { .status() .then((r) => { const statusData = (r || {}).data || {} - dispatch(updateApplication('Settings', 'locked', statusData.Locked)) + dispatch(updateApplication('Settings', 'status', statusData.Status)) dispatch(updateApplication('Settings', 'hash', statusData.Hash)) - if (statusData.Locked) { + if (statusData.Status == 'SUBMITTED') { locked = true env.History().push('/locked') } diff --git a/src/components/Section/Package/Submit/index.jsx b/src/components/Section/Package/Submit/index.jsx index 9a9b72cca..5d1867fd6 100644 --- a/src/components/Section/Package/Submit/index.jsx +++ b/src/components/Section/Package/Submit/index.jsx @@ -59,11 +59,11 @@ export class PackageSubmit extends React.Component { .then(() => api.status()) .then((response = {}) => { const statusData = (response).data || { - Locked: false, + Status: 'INCOMPLETE', Hash: false, } - updateApplication('Settings', 'locked', statusData.Locked) + updateApplication('Settings', 'status', statusData.Status) updateApplication('Settings', 'hash', statusData.Hash) history.push('/form/package/print') diff --git a/src/schema/section/submission.js b/src/schema/section/submission.js index e0f49dfe9..2030b8ab4 100644 --- a/src/schema/section/submission.js +++ b/src/schema/section/submission.js @@ -22,7 +22,6 @@ export const submission = (data = {}) => { }), Attachments: form.general('submission.attachments', { Method: form.text(attachmentType) - }), - Locked: data.Locked + }) } } diff --git a/src/selectors/navigation.js b/src/selectors/navigation.js index ac4af14fa..1de4377f4 100644 --- a/src/selectors/navigation.js +++ b/src/selectors/navigation.js @@ -8,6 +8,7 @@ import * as sections from 'constants/sections' import { hideSelectiveService } from 'validators/selectiveservice' import { hideDisciplinaryProcedures } from 'validators/militarydisciplinary' import { hideExistingConditions } from 'validators/psychological' +import { formIsLocked } from 'validators' import { formHasErrors, reduceSubsections } from 'helpers/navigation' @@ -57,27 +58,21 @@ const getSectionCompleted = (state, props) => { )) } -const getFormLocked = (state) => { - const { application } = state - const { Settings } = application - return Settings && Settings.locked -} - const getSectionLocked = (state, props) => { const { section } = props - const formIsLocked = getFormLocked(state) + const formLocked = formIsLocked(state.application) // Special cases switch (section.key) { case sections.REVIEW_AND_SUBMIT_SUBMIT: { - return formIsLocked || formHasErrors(state) + return formLocked || formHasErrors(state) } case sections.REVIEW_AND_SUBMIT_PRINT: - return !formIsLocked + return !formLocked default: - return formIsLocked + return formLocked } } diff --git a/src/validators/releases.js b/src/validators/releases.js index fc1ca6987..afea5daa0 100644 --- a/src/validators/releases.js +++ b/src/validators/releases.js @@ -31,5 +31,5 @@ export const formIsSigned = (signatures = {}, hideHippaSection) => { export const formIsLocked = (store = {}) => { const settings = store.Settings || {} - return settings.locked + return settings.status === 'SUBMITTED' } From ab35f48212ab791b986d4d7ade1dc4a59ac32a08 Mon Sep 17 00:00:00 2001 From: MacRae Linton <55759+macrael@users.noreply.github.com> Date: Tue, 25 Jun 2019 10:23:38 -0700 Subject: [PATCH 2/6] Add a kickback binary and have the status api result plumb through. --- api/cmd/kickback/main.go | 22 ++++++++++++++++++++++ src/actions/ApplicationActions.js | 1 - 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 api/cmd/kickback/main.go diff --git a/api/cmd/kickback/main.go b/api/cmd/kickback/main.go new file mode 100644 index 000000000..76cbf7caa --- /dev/null +++ b/api/cmd/kickback/main.go @@ -0,0 +1,22 @@ +package main + +import ( + "github.com/18F/e-QIP-prototype/api" + "github.com/18F/e-QIP-prototype/api/admin" + "github.com/18F/e-QIP-prototype/api/cmd" + "github.com/18F/e-QIP-prototype/api/log" +) + +func main() { + logger := &log.Service{Log: log.NewLogger()} + cmd.Command(logger, func(context api.DatabaseService, store api.StorageService, account *api.Account) { + rejector := admin.NewRejecter(context, store, nil) + + rejectErr := rejector.Reject(*account) + if rejectErr != nil { + logger.WarnError("Failed to kickback", rejectErr, api.LogFields{"account": account.Username}) + } else { + logger.Warn("Account kicked back", api.LogFields{"account": account.Username}) + } + }) +} diff --git a/src/actions/ApplicationActions.js b/src/actions/ApplicationActions.js index c921645a3..c1294a34a 100644 --- a/src/actions/ApplicationActions.js +++ b/src/actions/ApplicationActions.js @@ -51,7 +51,6 @@ export function getApplicationState(done) { dispatch(updateApplication('Settings', 'formType', formType)) dispatch(updateApplication('Settings', 'formVersion', formData.Metadata.form_version)) - dispatch(updateApplication('Settings', 'status', window.status)) dispatch(setFormData(formData, done)) }) }) From 32e9064686caa66d5ae6c4656ea127557e19650c Mon Sep 17 00:00:00 2001 From: MacRae Linton <55759+macrael@users.noreply.github.com> Date: Tue, 25 Jun 2019 10:31:58 -0700 Subject: [PATCH 3/6] Remove defunct error --- api/account.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/api/account.go b/api/account.go index cc0c64d40..6e1b05fa8 100644 --- a/api/account.go +++ b/api/account.go @@ -15,9 +15,6 @@ var ( // ErrDatastoreConnection is an error when a database connection cannot be made ErrDatastoreConnection = errors.New("Unable to connect to datastore") - - // ErrInvalidStatusTransition is an error when a database connection cannot be made - ErrInvalidStatusTransition = errors.New("Invalid Status Transition") ) // knownFormVersions is a map of FormType -> slice of known versions sorted with most recent first. From 5e27e3a1a04334c1f0a43fa481c29489df38505e Mon Sep 17 00:00:00 2001 From: MacRae Linton <55759+macrael@users.noreply.github.com> Date: Tue, 25 Jun 2019 10:34:29 -0700 Subject: [PATCH 4/6] Fix missing linter comments --- api/account.go | 9 +++++++-- api/admin/reject.go | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/api/account.go b/api/account.go index 6e1b05fa8..cc740f3d1 100644 --- a/api/account.go +++ b/api/account.go @@ -30,9 +30,12 @@ var knownFormVersions = map[string][]string{ } const ( + // StatusIncomplete indicates that the submission is INCOMPLETE StatusIncomplete = "INCOMPLETE" - StatusSubmitted = "SUBMITTED" - StatusKickback = "KICKBACK" + // StatusSubmitted indicates that the submission is SUBMITTED + StatusSubmitted = "SUBMITTED" + // StatusKickback indicates that the submission is KICKBACK + StatusKickback = "KICKBACK" ) // Account represents a user account @@ -139,6 +142,7 @@ func (entity *Account) FindByExternalID(context DatabaseService) error { return context.Where(entity, "Account.external_id = ?", entity.ExternalID) } +// CanSubmit returns wether the account is in a valid state for submission func (entity *Account) CanSubmit() bool { if entity.Status == StatusSubmitted { return false @@ -161,6 +165,7 @@ func (entity *Account) Unsubmit() { entity.Status = StatusIncomplete } +// CanKickback returns wether the account is in a valid state for kickback func (entity *Account) CanKickback() bool { if entity.Status != StatusSubmitted { return false diff --git a/api/admin/reject.go b/api/admin/reject.go index b561e3051..565ae740b 100644 --- a/api/admin/reject.go +++ b/api/admin/reject.go @@ -54,7 +54,7 @@ func (r Rejecter) Reject(account api.Account) error { ok := account.Kickback() if !ok { - return errors.New("The account got into a bad state during the rejection.") + return errors.New("The account got into a bad state during the rejection") } _, saveAccErr := account.Save(r.db, account.ID) From 3bfb4b2b863139944f3ec3e87023992f45754bac Mon Sep 17 00:00:00 2001 From: MacRae Linton <55759+macrael@users.noreply.github.com> Date: Tue, 25 Jun 2019 11:24:08 -0700 Subject: [PATCH 5/6] fix test --- src/schema/section/submission.test.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/schema/section/submission.test.js b/src/schema/section/submission.test.js index d2ec8cad6..22077adf6 100644 --- a/src/schema/section/submission.test.js +++ b/src/schema/section/submission.test.js @@ -30,8 +30,7 @@ describe('Schema for financial taxes', () => { Name: {}, Date: {} } - }, - Locked: null + } } expect(unschema(submission(data))).toEqual(data) From 28d7e1dce625484b3a6b7952561c3a642b71ecc0 Mon Sep 17 00:00:00 2001 From: Suzanne Rozier Date: Tue, 25 Jun 2019 16:47:10 -0400 Subject: [PATCH 6/6] Convert application statuses to constants in JS --- src/actions/ApplicationActions.js | 3 ++- src/constants/enums/applicationStatuses.js | 3 +++ src/validators/releases.js | 3 ++- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 src/constants/enums/applicationStatuses.js diff --git a/src/actions/ApplicationActions.js b/src/actions/ApplicationActions.js index c1294a34a..b8ffa5d5c 100644 --- a/src/actions/ApplicationActions.js +++ b/src/actions/ApplicationActions.js @@ -1,5 +1,6 @@ import { env } from 'config' import { api } from 'services' +import { STATUS_SUBMITTED } from 'constants/enums/applicationStatuses' import * as actionTypes from 'constants/actionTypes' @@ -33,7 +34,7 @@ export function getApplicationState(done) { dispatch(updateApplication('Settings', 'status', statusData.Status)) dispatch(updateApplication('Settings', 'hash', statusData.Hash)) - if (statusData.Status == 'SUBMITTED') { + if (statusData.Status === STATUS_SUBMITTED) { locked = true env.History().push('/locked') } diff --git a/src/constants/enums/applicationStatuses.js b/src/constants/enums/applicationStatuses.js new file mode 100644 index 000000000..bca3741e9 --- /dev/null +++ b/src/constants/enums/applicationStatuses.js @@ -0,0 +1,3 @@ +export const STATUS_INCOMPLETE = 'INCOMPLETE' +export const STATUS_SUBMITTED = 'SUBMITTED' +export const STATUS_KICKBACK = 'KICKBACK' diff --git a/src/validators/releases.js b/src/validators/releases.js index afea5daa0..89893b2d9 100644 --- a/src/validators/releases.js +++ b/src/validators/releases.js @@ -1,3 +1,4 @@ +import { STATUS_SUBMITTED } from 'constants/enums/applicationStatuses' import SignatureValidator from './signature' export const hideHippa = (store = {}) => { @@ -31,5 +32,5 @@ export const formIsSigned = (signatures = {}, hideHippaSection) => { export const formIsLocked = (store = {}) => { const settings = store.Settings || {} - return settings.status === 'SUBMITTED' + return settings.status === STATUS_SUBMITTED }