Skip to content

Commit

Permalink
Merge pull request #4610 from bcgov/dev
Browse files Browse the repository at this point in the history
8.4.5 Release
  • Loading branch information
milosdes authored Oct 26, 2023
2 parents 40a589c + d351da6 commit 9035cca
Show file tree
Hide file tree
Showing 18 changed files with 261 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,8 @@ const BottomButtonGroup = React.memo(
case StateEnum.tagging.name:
case StateEnum.readytoscan.name:
case StateEnum.peerreview.name:
case StateEnum.section5pending.name:
case StateEnum.onholdapplicationfee.name:
const status = Object.values(StateEnum).find(
(statusValue) => statusValue.name === currentSelectedStatus
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
background-color: #C45303;
}

.foitabheaderIntakeInProgressBG {
.foitabheaderIntakeInProgressBG, .foitabheaderOnHoldApplicationFeeBG, .foitabheaderSection5Pending {
background-color: #8C3601;
}

Expand Down
7 changes: 6 additions & 1 deletion forms-flow-web/src/components/FOI/FOIRequest/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,12 @@ export const getTabBG = (_tabStatus, _requestState) => {
case StateEnum.tagging.name:
return "foitabheadercollection foitabheaderTaggingBG";
case StateEnum.readytoscan.name:
return "foitabheadercollection foitabheaderReadytoScanBG";
return "foitabheadercollection foitabheaderReadytoScanBG";
case StateEnum.section5pending.name:
return "foitabheadercollection foitabheaderSection5Pending";
case StateEnum.onholdapplicationfee.name:
return "foitabheadercollection foitabheaderOnHoldApplicationFeeBG";

default:
return "foitabheadercollection foitabheaderdefaultBG";
}
Expand Down
2 changes: 1 addition & 1 deletion forms-flow-web/src/components/FOI/Header/FOIHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ const adminDashboard = (_e) => {
</li>
}
<li className="help-icon foinavitem">
<a href={"https://help.foirequests.gov.bc.ca/"} target="_blank" aria-label="foi-help link">
<a href={"https://help.foirequests.gov.bc.ca/help-articles"} target="_blank" aria-label="foi-help link">
<HelpOutlineIcon style={{fontSize: "21px", color: "white", textDecoration: "none", cursor: "pointer"}} />
</a>
</li>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ import { getFullnameList } from "../../../../helper/FOI/helper";
return {title: "Close Request", body: ""};
case StateEnum.redirect.name.toLowerCase():
return {title: "Redirect Request", body: "Are you sure you want to Redirect this request?"};
case StateEnum.section5pending.name.toLowerCase():
return {title: "Changing the state", body: `Are you sure you want to change Request #${_requestNumber} to ${StateEnum.section5pending.name}?`};
case StateEnum.callforrecords.name.toLowerCase():
return {title: "Changing the state", body: `Are you sure you want to change Request #${_requestNumber} to ${StateEnum.callforrecords.name}?`};
case StateEnum.review.name.toLowerCase():
Expand Down Expand Up @@ -124,6 +126,8 @@ import { getFullnameList } from "../../../../helper/FOI/helper";
return {title: "Ministry Sign Off", body: `Upload eApproval Logs, and enter in required approval fields to verify Ministry Approval and then change the state.`};
else
return {title: "Changing the state", body: `Are you sure you want to change Request #${_requestNumber} to ${StateEnum.response.name}?`};
case StateEnum.onholdapplicationfee.name.toLowerCase():
return {title: "Changing the state", body: `Are you sure you want to change Request #${_requestNumber} to ${StateEnum.onholdapplicationfee.name}?`};
default:
return {title: "", body: ""};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,11 @@ const StateDropDown = ({
case StateEnum.unopened.name.toLowerCase():
return _stateList.unopened;
case StateEnum.intakeinprogress.name.toLowerCase():
return _stateList.intakeinprogress;
if (personalIAO) {
return _stateList.intakeinprogressforpersonals;
} else {
return _stateList.intakeinprogress;
}
case StateEnum.peerreview.name.toLowerCase():
if(!isMinistryCoordinator){
//const currentStatusVersion = stateTransition[0]?.version;
Expand Down Expand Up @@ -166,6 +170,13 @@ const StateDropDown = ({
else {
return _stateList.response.filter(val => val.status.toLowerCase() !== StateEnum.onhold.name.toLowerCase());
}
case StateEnum.section5pending.name.toLowerCase():
if (personalIAO) {
return _stateList.section5pending;
}
break
case StateEnum.onholdapplicationfee.name.toLowerCase():
return _stateList.onholdapplicationfee;

default:
return [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
background-color: #C45303;
}

.intakeinprogress {
.intakeinprogress, .section5pending {
background-color: #8C3601;
}

Expand Down Expand Up @@ -62,6 +62,10 @@
background-color: #595959;
}

.on-hold-applicationfee {
background-color: #8C3601;
}

.harmsassessment {
background-color: #832AB7;
}
Expand Down
12 changes: 9 additions & 3 deletions forms-flow-web/src/constants/FOI/statusEnum.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const StateList = Object.freeze({
unopened: [{status: "Unopened", isSelected: false}, {status:"Intake in Progress", isSelected: false}],
intakeinprogress: [{status:"Intake in Progress", isSelected: false}, {status: "Open", isSelected: false}, {status:"Peer Review", isSelected: false}, {status: "Redirect", isSelected: false}, {status: "Closed", isSelected: false}],
intakeinprogress: [{status:"Intake in Progress", isSelected: false}, {status: "Open", isSelected: false}, {status:"Peer Review", isSelected: false}, {status: "Redirect", isSelected: false}, {status: "On-Hold - Application Fee", isSelected: false}, {status: "Closed", isSelected: false}],
intakeinprogressforpersonals: [{status:"Intake in Progress", isSelected: false}, {status: "Open", isSelected: false}, {status:"Peer Review", isSelected: false}, {status: "Redirect", isSelected: false}, {status: "Section 5 Pending", isSelected: false}, {status: "Closed", isSelected: false}],
redirect: [{status: "Redirect", isSelected: false}, {status:"Intake in Progress", isSelected: false}, {status: "Closed", isSelected: false}],
open: [{status: "Open", isSelected: false}, {status: "Call For Records", isSelected: false}, {status:"Peer Review", isSelected: false},{status: "Closed", isSelected: false}],
callforrecords: [{status: "Call For Records", isSelected: false}, {status: "Open", isSelected: false}, {status: "Closed", isSelected: false}],
Expand All @@ -19,7 +20,9 @@ const StateList = Object.freeze({
response: [{status: "Response", isSelected: false}, {status: "On Hold", isSelected: false}, {status: "Records Review", isSelected: false}, {status:"Peer Review", isSelected: false}, {status: "Closed", isSelected: false}],
responseforpersonal: [{status: "Response", isSelected: false}, {status: "Records Review", isSelected: false}, {status:"Peer Review", isSelected: false}, {status: "Closed", isSelected: false}],
//peerreview: [{status:"Peer Review", isSelected: false},{status:"Intake in Progress", isSelected: false}, {status: "Open", isSelected: false},{status: "Records Review", isSelected: false},{status: "Consult", isSelected: false},{status: "Response", isSelected: false}],
peerreview: [{status:"Peer Review", isSelected: false}]
peerreview: [{status:"Peer Review", isSelected: false}],
section5pending: [{status: "Section 5 Pending", isSelected: false}, {status: "Open", isSelected: false}, {status:"Peer Review", isSelected: false}, {status: "Redirect", isSelected: false}, {status: "Closed", isSelected: false}],
onholdapplicationfee: [{status: "On-Hold - Application Fee", isSelected: false}, {status: "Open", isSelected: false}, {status:"Peer Review", isSelected: false}, {status: "Redirect", isSelected: false}, {status: "Closed", isSelected: false}]
});

const MinistryStateList = Object.freeze({
Expand All @@ -43,6 +46,7 @@ const MinistryStateList = Object.freeze({
readytoscan : [{status: "Ready to Scan", isSelected: true}]
});

// This corresponds to rows in the FOIRequestStatuses table on the backend
const StateEnum = Object.freeze({
open: {name: "Open", id: 1},
callforrecords: {name: "Call For Records", id: 2},
Expand All @@ -62,7 +66,9 @@ const StateEnum = Object.freeze({
archived: {name: "Archived", id: 15},
peerreview: {name: "Peer Review", id: 16},
tagging: {name: "Tagging", id: 17},
readytoscan: {name: "Ready to Scan", id: 18}
readytoscan: {name: "Ready to Scan", id: 18},
onholdapplicationfee: {name: "On-Hold - Application Fee", id: 19},
section5pending: {name: "Section 5 Pending", id: 20},
});

const StateTransitionCategories = Object.freeze({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Add On-Hold Application-Fee state to FOIRequestStatuses
Revision ID: 1491b3126887
Revises: aacdbca19a47
Create Date: 2023-09-26 12:46:12.187207
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql

# revision identifiers, used by Alembic.
revision = '1491b3126887'
down_revision = 'aacdbca19a47'
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
sql = '''INSERT INTO "FOIRequestStatuses" (requeststatusid, name, description, isactive) VALUES (19, 'On-Hold - Application Fee', 'On Hold for Application Fee', true)'''
op.execute(sql)


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
sql = '''DELETE FROM "FOIRequestStatuses" WHERE requeststatusid = 19'''
op.execute(sql)
26 changes: 26 additions & 0 deletions request-management-api/migrations/versions/a79cd809e85e_.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""Adding Section 5 Pending FOI Status to FOIRequestStatuses Table and NotificaitonTypes Table
Revision ID: a79cd809e85e
Revises: 1491b3126887
Create Date: 2023-09-25 14:37:29.208839
"""
from alembic import op
from sqlalchemy.dialects import postgresql

# revision identifiers, used by Alembic.
revision = 'a79cd809e85e'
down_revision = '1491b3126887'
branch_labels = None
depends_on = None


def upgrade():
op.execute('INSERT INTO public."FOIRequestStatuses"(requeststatusid, name, description, isactive) VALUES (20, \'Section 5 Pending\', \'Section 5 Pending (Personal)\', true);commit;')
op.execute('INSERT INTO public."NotificationTypes"(notificationtypeid, name, description, isactive) VALUES (20, \'Section 5 Pending Reminder\', \'Section 5 Pending Reminder\', true);commit;')



def downgrade():
op.execute('DELETE FROM public."FOIRequestStatuses" WHERE requeststatusid = 20;commit;')
op.execute('DELETE FROM public."NotificationTypes" WHERE notificationtypeid = 20;commit;')
39 changes: 39 additions & 0 deletions request-management-api/request_api/models/FOIRawRequests.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,26 @@ def getassignmenttransition(cls,requestid):
db.session.close()
return assignments

@classmethod
def getonholdapplicationfeerequests(cls): # with the reminder date
onholdapplicationfeerequests = []
try:
sql = '''
SELECT * FROM (SELECT DISTINCT ON (requestid) requestid, updated_at, status FROM public."FOIRawRequests"
ORDER BY requestid ASC, version DESC) r
WHERE r.status = 'On-Hold - Application Fee'
AND r.updated_at::date < NOW()::date - INTERVAL '15 DAY'
order by r.updated_at asc
'''
rs = db.session.execute(text(sql))
onholdapplicationfeerequests = rs
except Exception as ex:
logging.error(ex)
raise ex
finally:
db.session.close()
return onholdapplicationfeerequests

@classmethod
def getversionforrequest(cls,requestid):
return db.session.query(FOIRawRequest.version).filter_by(requestid=requestid).order_by(FOIRawRequest.version.desc()).first()
Expand Down Expand Up @@ -981,6 +1001,25 @@ def getmetadata(cls,requestid):
finally:
db.session.close()
return requestdetails

@classmethod
def getlatestsection5pendings(cls):
section5pendings = []
try:
sql = """SELECT * FROM
(SELECT DISTINCT ON (requestid) requestid, created_at, version, status, axisrequestid
FROM public."FOIRawRequests"
ORDER BY requestid ASC, version DESC) foireqs
WHERE foireqs.status = 'Section 5 Pending';"""
rs = db.session.execute(text(sql))
for row in rs:
section5pendings.append({"requestid": row["requestid"], "version": row["version"], "statusname": row["status"], "created_at": row["created_at"], "axisrequestid": ["axisrequestid"]})
except Exception as ex:
logging.error(ex)
raise ex
finally:
db.session.close()
return section5pendings

class FOIRawRequestSchema(ma.Schema):
class Meta:
Expand Down
7 changes: 4 additions & 3 deletions request-management-api/request_api/resources/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ def post(requestid=None, actiontype=None):

if int(requestid) and str(requestid) != "-1" :
status = rawrequestservice().getstatus(updaterequest)
rawrequest = rawrequestservice().getrawrequest(requestid)
if status not in ['Intake in Progress', 'Closed', 'Redirect', 'Peer Review', 'Section 5 Pending', 'On-Hold - Application Fee']:
raise ValueError('Invalid request state.')
result = rawrequestservice().saverawrequestversion(updaterequest,requestid,assigneegroup,assignee,status,AuthHelper.getuserid(),assigneefirstname,assigneemiddlename,assigneelastname, actiontype)
assignee = ''
if(actiontype == 'assignee'):
Expand All @@ -110,8 +111,8 @@ def post(requestid=None, actiontype=None):
assignee = getassignee(assigneefirstname,assigneelastname,assigneegroup)
asyncio.ensure_future(eventservice().postevent(result.identifier,"rawrequest",AuthHelper.getuserid(),AuthHelper.getusername(),AuthHelper.isministrymember(),assignee))
return {'status': result.success, 'message':result.message,'id':result.identifier} , 200
except ValueError:
return {'status': 500, 'message':INVALID_REQUEST_ID}, 500
except ValueError as valuexception:
return {'status': 500, 'message':str(valuexception)}, 500
except BusinessException as exception:
return {'status': exception.status_code, 'message':exception.message}, 500

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def updateapplicantcorrespondencelog(self, correspondenceid, content):
def getapplicantcorrespondencelogbyid(self, applicantcorrespondenceid):
applicantcorrespondence = FOIApplicantCorrespondence.getapplicantcorrespondencebyid(applicantcorrespondenceid)
(_correspondencemessagejson, _isjson) = self.__getjsonobject(applicantcorrespondence["correspondencemessagejson"])
emailhtml_decoded_string = html.unescape(self.__getvaluefromjson(_correspondencemessagejson, 'emailhtml'))
emailhtml_decoded_string = html.unescape(self.__getvaluefromjson(_correspondencemessagejson, 'emailhtml'))
return emailhtml_decoded_string if _isjson else _correspondencemessagejson

def getlatestapplicantcorrespondence(self, ministryid):
Expand Down
49 changes: 48 additions & 1 deletion request-management-api/request_api/services/events/payment.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from request_api.services.commentservice import commentservice
from request_api.services.notificationservice import notificationservice
from request_api.models.FOIMinistryRequests import FOIMinistryRequest
from request_api.models.FOIRawRequests import FOIRawRequest
from request_api.models.FOIRequestStatus import FOIRequestStatus
import json
from request_api.models.default_method_result import DefaultMethodResult
Expand All @@ -13,6 +14,10 @@
from dateutil.parser import parse
from pytz import timezone
from request_api.utils.enums import PaymentEventType
from request_api.utils.commons.datetimehandler import datetimehandler
from request_api.services.commons.duecalculator import duecalculator
from request_api.exceptions import BusinessException
from flask import current_app

class paymentevent:
""" FOI Event management service
Expand All @@ -34,6 +39,38 @@ def createpaymentexpiredevent(self, requestid):
else:
return DefaultMethodResult(False,'Unable to post Payment Expiry notification',requestid)

def createpaymentreminderevent(self):
try:
_today = datetimehandler().gettoday()

notificationservice().dismissremindernotification("rawrequest", self.__notificationtype())
eventtype = PaymentEventType.reminder.value
_onholdrequests = FOIRawRequest.getonholdapplicationfeerequests()
for entry in _onholdrequests:
_dateofstatechange = datetimehandler().formatdate(entry['updated_at'])
businessdayselapsed = duecalculator().getbusinessdaysbetween(_dateofstatechange)
if businessdayselapsed >= 20 and duecalculator().isbusinessday(_today):
commentexists = False
existingcomments = commentservice().getrawrequestcomments(entry['requestid'])
for comment in existingcomments:
if comment['text'] == self.__preparecomment(entry['requestid'], eventtype)['comment']: #checks if comment already exists
commentexists = True
if not commentexists:
self.__createcommentforrawrequest(entry['requestid'], eventtype)
self.__createnotificationforrawrequest(entry['requestid'], eventtype)
return DefaultMethodResult(True,'Payment reminder notifications created',_today)
except BusinessException as exception:
current_app.logger.error("%s,%s" % ('Payment reminder Notification Error', exception.message))
return DefaultMethodResult(False,'Payment reminder notifications failed', _today)

def __createcommentforrawrequest(self, requestid, eventtype):
comment = self.__preparecomment(requestid, eventtype)
return commentservice().createrawrequestcomment(comment, "system", 2)

def __createnotificationforrawrequest(self, requestid, eventtype):
notification = self.__preparenotification(requestid, eventtype)
return notificationservice().createnotification({"message" : notification}, requestid, "rawrequest", self.__notificationtype(), "system")

def __createcomment(self, requestid, eventtype):
comment = self.__preparecomment(requestid, eventtype)
return commentservice().createministryrequestcomment(comment, self.__defaultuserid(), 2)
Expand All @@ -54,10 +91,15 @@ def __preparecomment(self, requestid, eventtype):
comment = {"comment": "Applicant has paid outstanding fee. Response package can be released."}
elif eventtype == PaymentEventType.depositpaid.value:
comment = {"comment": "Applicant has paid deposit. New LDD is " + FOIMinistryRequest.getduedate(requestid).strftime("%m/%d/%Y")}
elif eventtype == PaymentEventType.reminder.value:
comment = {"comment": "20 business days has passed awaiting payment, you can consider closing the request as abandoned"}
else:
comment = None
if comment is not None:
comment['ministryrequestid']= requestid
if eventtype == PaymentEventType.reminder.value:
comment['requestid'] = requestid
else:
comment['ministryrequestid']= requestid
return comment

def __notificationmessage(self, requestid, eventtype):
Expand All @@ -69,6 +111,8 @@ def __notificationmessage(self, requestid, eventtype):
return "Applicant has paid outstanding fee. Response package can be released."
elif eventtype == PaymentEventType.depositpaid.value:
return "Applicant has paid deposit. New LDD is " + FOIMinistryRequest.getduedate(requestid).strftime("%m/%d/%Y")
elif eventtype == PaymentEventType.reminder.value:
return "20 business days has passed awaiting payment, you can consider closing the request as abandoned"
else:
return None

Expand All @@ -78,3 +122,6 @@ def __defaultuserid(self):
def gettoday(self):
now_pst = maya.parse(maya.now()).datetime(to_timezone='America/Vancouver', naive=False)
return now_pst.strftime('%m/%d/%Y')

def __notificationtype(self):
return "Payment"
Loading

0 comments on commit 9035cca

Please sign in to comment.