diff --git a/api/app/admin/office.py b/api/app/admin/office.py index f76b8f6bd..620033a00 100644 --- a/api/app/admin/office.py +++ b/api/app/admin/office.py @@ -68,13 +68,13 @@ def get_query(self): 'appointments_days_limit', 'appointment_duration', 'soonest_appointment', 'max_person_appointment_per_day',\ 'civic_address', 'telephone', 'online_status', 'check_in_notification', 'check_in_reminder_msg',\ 'automatic_reminder_at', 'currently_waiting', 'digital_signage_message', 'digital_signage_message_1',\ - 'digital_signage_message_2', 'digital_signage_message_3', 'show_currently_waiting_bottom' ) + 'digital_signage_message_2', 'digital_signage_message_3', 'show_currently_waiting_bottom', 'optout_status' ) form_edit_rules = ('office_name', 'office_number', 'sb', 'services', 'deleted', 'exams_enabled_ind', 'appointments_enabled_ind', 'timezone', 'latitude', 'longitude', 'office_appointment_message', 'appointments_days_limit', 'appointment_duration', 'soonest_appointment', 'max_person_appointment_per_day',\ 'civic_address', 'telephone', 'online_status', 'check_in_notification', 'check_in_reminder_msg', \ 'automatic_reminder_at', 'currently_waiting', 'digital_signage_message', 'digital_signage_message_1',\ - 'digital_signage_message_2', 'digital_signage_message_3', 'show_currently_waiting_bottom' ) + 'digital_signage_message_2', 'digital_signage_message_3', 'show_currently_waiting_bottom', 'optout_status' ) form_choices = { 'exams_enabled_ind': [ ("0", 'No - Exams are not enabled for this office'), \ @@ -106,6 +106,10 @@ def get_query(self): ("0", 'Off - Hide Currently Waiting at bottom in Smartboard'), \ ("1", 'On - Show Currently Waiting from bottom in Smartboard') ], + 'optout_status': [ + ("0", 'Off - Open tickets automatically closed at end of day'), \ + ("1", 'On - Tickets must be closed manually') + ], } # Defining String constants to appease SonarQube timezone_name_const = 'timezone.timezone_name' @@ -140,6 +144,7 @@ def get_query(self): 'digital_signage_message_2', 'digital_signage_message_3', 'show_currently_waiting_bottom', + 'optout_status', ] column_list_support = ['office_name', @@ -170,6 +175,7 @@ def get_query(self): 'digital_signage_message_2', 'digital_signage_message_3', 'show_currently_waiting_bottom', + 'optout_status', ] form_excluded_columns = ('citizens', @@ -213,6 +219,7 @@ def get_query(self): 'number_of_dlkt', 'timeslots', 'deleted', + 'optout_status', ) form_edit_rules = ('office_name', @@ -249,6 +256,7 @@ def get_query(self): 'number_of_dlkt', 'timeslots', 'deleted', + 'optout_status', ) form_args = { @@ -388,7 +396,8 @@ class OfficeConfigGA(OfficeConfig): 'appointments_days_limit', 'soonest_appointment', 'max_person_appointment_per_day', - 'number_of_dlkt', + 'number_of_dlkt', + 'optout_status' , ) form_excluded_columns = ( diff --git a/api/app/models/theq/office.py b/api/app/models/theq/office.py index e4916819e..4ac1218a1 100644 --- a/api/app/models/theq/office.py +++ b/api/app/models/theq/office.py @@ -81,6 +81,7 @@ class Office(Base): appointment_duration = db.Column(db.Integer, default=30) max_person_appointment_per_day = db.Column(db.Integer, default=1) civic_address = db.Column(db.String(200)) + optout_status = db.Column(db.Integer, default=0) telephone = db.Column(db.String(20)) online_status = db.Column(Enum(Status)) number_of_dlkt = db.Column(db.Integer, nullable=True) diff --git a/api/app/schemas/theq/office_schema.py b/api/app/schemas/theq/office_schema.py index 6dc35dfc6..9dc52d3f9 100644 --- a/api/app/schemas/theq/office_schema.py +++ b/api/app/schemas/theq/office_schema.py @@ -49,6 +49,7 @@ class Meta(BaseSchema.Meta): office_appointment_message = fields.Str() civic_address = fields.Str() online_status = fields.Str() + optout_status = fields.Int() external_map_link = fields.Str() # for walk-in notifications diff --git a/api/migrations/README b/api/migrations/README index 98e4f9c44..c8abd2423 100755 --- a/api/migrations/README +++ b/api/migrations/README @@ -1 +1,15 @@ -Generic single-database configuration. \ No newline at end of file +Generic single-database configuration. + +Run Migrations: + +// make changes to your model +run "flask db migrate -m "ticket_no_description" +OR +run "python manage.py db migrate -m "ticket_no_description" + +//wait for it to generate xyz_ticket_no_description.py in Migrations folder +// check on the auto generated migration script verify/edit changes as required +run "flask db upgrade" to apply changes + +run "flask db downgrade" to rollback the latest changes + diff --git a/api/migrations/versions/4bf4b127aaa4_sbcq155.py b/api/migrations/versions/4bf4b127aaa4_sbcq155.py new file mode 100644 index 000000000..433a06e32 --- /dev/null +++ b/api/migrations/versions/4bf4b127aaa4_sbcq155.py @@ -0,0 +1,33 @@ +"""sbcq155 + +Revision ID: 4bf4b127aaa4 +Revises: ba295f379d0d +Create Date: 2024-07-26 09:41:48.557375 + +""" +from alembic import op +import sqlalchemy as sa +import sqlalchemy_utc +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = '4bf4b127aaa4' +down_revision = 'ba295f379d0d' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + + op.add_column('office', sa.Column('optout_status', sa.Integer(), nullable=False, server_default='0')) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + + op.drop_column('office', 'optout_status') + + # ### end Alembic commands ### diff --git a/api/migrations/versions/8b6c67545310_sbcq156.py b/api/migrations/versions/8b6c67545310_sbcq156.py new file mode 100644 index 000000000..8810e7ef8 --- /dev/null +++ b/api/migrations/versions/8b6c67545310_sbcq156.py @@ -0,0 +1,39 @@ +"""sbcq156 + +Revision ID: 8b6c67545310 +Revises: 4bf4b127aaa4 +Create Date: 2024-07-26 10:13:02.148021 + +""" +from alembic import op +import sqlalchemy as sa +import sqlalchemy_utc +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = '8b6c67545310' +down_revision = '4bf4b127aaa4' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + + op.alter_column('office', 'optout_status', + existing_type=sa.INTEGER(), + nullable=True, + existing_server_default=sa.text('0')) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + + op.alter_column('office', 'optout_status', + existing_type=sa.INTEGER(), + nullable=False, + existing_server_default=sa.text('0')) + + # ### end Alembic commands ### diff --git a/api/qsystem.py b/api/qsystem.py index d9770f899..a3cb4a72a 100644 --- a/api/qsystem.py +++ b/api/qsystem.py @@ -24,6 +24,7 @@ from sqlalchemy import event from sqlalchemy.engine import Engine from sqlalchemy_continuum import make_versioned +from flask_migrate import Migrate def my_print(my_data): @@ -46,6 +47,8 @@ def time_string(): ms = now.strftime("%f")[:3] now_string = now.strftime("%Y-%m-%d %H:%M:%S,") return "[" + now_string + ms + "] " +migrate = Migrate() + application = Flask(__name__, instance_relative_config=True) @@ -74,6 +77,7 @@ def time_string(): cache.init_app(application) ma = Marshmallow(application) +migrate.init_app(application, db) make_versioned(user_cls=None, plugins=[]) diff --git a/api/sqlcode/Q2-CloseOpenTicketsFunctionDefinition.sql b/api/sqlcode/Q2-CloseOpenTicketsFunctionDefinition.sql index 8193fc53c..76d166e2c 100644 --- a/api/sqlcode/Q2-CloseOpenTicketsFunctionDefinition.sql +++ b/api/sqlcode/Q2-CloseOpenTicketsFunctionDefinition.sql @@ -64,7 +64,7 @@ begin set cs_id = id_got_services, accurate_time_ind = 0, citizen_comments = '' - where citizen.citizen_id in + where office_id in (select office_id from office where optout_status = 0) and citizen.citizen_id in ( select distinct f.cid from @@ -94,19 +94,33 @@ begin set cs_id = id_got_services, accurate_time_ind = 0, citizen_comments = '' - where cs_id = (select cs_id from citizenstate where cs_state_name = 'Active'); + where cs_id = (select cs_id from citizenstate where cs_state_name = 'Active') and office_id in (select office_id from office where optout_status = 0); get diagnostics update_active = ROW_COUNT; /* Set period time_end to be now for all periods with null ending time. */ - update period - set time_end = now() - where time_end is null; + + UPDATE period + SET time_end = NOW() + WHERE time_end IS NULL + AND sr_id IN ( + SELECT sr_id + FROM servicereq + WHERE citizen_id IN ( + SELECT citizen_id + FROM citizen + WHERE office_id in (select office_id from office where optout_status = 0) + )); get diagnostics update_period = ROW_COUNT; /* Set all service requests to be complete, for those that aren't marked as complete. */ update servicereq set sr_state_id = (select sr_state_id from srstate where sr_code = 'Complete') - where sr_state_id != (select sr_state_id from srstate where sr_code = 'Complete'); + where sr_state_id != (select sr_state_id from srstate where sr_code = 'Complete') and + citizen_id in ( + SELECT citizen_id + FROM citizen + WHERE office_id in (select office_id from office where optout_status = 0)); + get diagnostics update_sr = ROW_COUNT; return_message = 'Tickets closed first pass: ' || update_citizen::text diff --git a/api/version.py b/api/version.py index 3509bd885..64778fc22 100644 --- a/api/version.py +++ b/api/version.py @@ -1 +1 @@ -__version__ = '1.1.3' \ No newline at end of file +__version__ = '1.1.4' \ No newline at end of file