def request_refund(self, nr_model: RequestDAO, payment_id: int): """ Processes a SINGLE refund request. This is different from the 'refund' in the NameRequest resource PATCH namerequests/{nrId}/REQUEST_REFUND which cancels the NR and refunds any associated payments. :param nr_model: :param payment_id: :return: """ # Handle the payments valid_states = [ PaymentState.APPROVED.value, PaymentState.COMPLETED.value, PaymentState.PARTIAL.value ] if nr_model.stateCd not in [State.DRAFT]: raise PaymentServiceError( message='Invalid NR state for cancel and refund') # Cancel any payments associated with the NR for payment in nr_model.payments.all(): if payment.payment_status_code in valid_states and payment.id == payment_id: # refund_payment(payment.payment_token, {'reason': 'Name Request user requested refund'}) refund_payment(payment.payment_token, {}) payment.payment_status_code = PaymentState.REFUND_REQUESTED.value payment.save_to_db() nr_svc = self.nr_service EventRecorder.record( nr_svc.user, Event.PATCH + f' [payment refunded] {payment.payment_action}', nr_model, nr_model.json()) return nr_model
def handle_patch_rollback(self, nr_model: Request, action: str): """ Roll back the Name Request. :param nr_model: :param action: :return: """ nr_svc = self.nr_service # This handles updates if the NR state is 'patchable' nr_model = self.update_nr(nr_model, State.CANCELLED, self.handle_nr_patch) # Only update the record in NRO if it's a real NR, otherwise the record won't exist if not is_temp_nr_num(nr_model.nrNum): # This handles the updates for NRO and Solr, if necessary # self.update_records_in_network_services(nr_model, update_solr=True) nr_model = self.update_request_in_nro(nr_model, self.save_nr) # Delete in solr for temp or real NR because it is cancelled if nr_model.entity_type_cd in [ 'CR', 'UL', 'BC', 'CP', 'PA', 'XCR', 'XUL', 'XCP', 'CC', 'FI', 'XCR', 'XUL', 'XCP' ]: SOLR_CORE = 'possible.conflicts' self.delete_solr_doc(SOLR_CORE, nr_model.nrNum) # Record the event EventRecorder.record(nr_svc.user, Event.PATCH + ' [rollback]', nr_model, nr_model.json()) return nr_model
def test_edit_inprogress_event_history(client, jwt, app): from namex.models import Request as RequestDAO, State, Name as NameDAO, User, Event from namex.services import EventRecorder # add a user for the comment user = User('test-user', '', '', '43e6a245-0bf7-4ccf-9bd0-e7fb85fd18cc', 'https://sso-dev.pathfinder.gov.bc.ca/auth/realms/sbc') user.save_to_db() headers = create_header(jwt, [User.EDITOR]) nr = RequestDAO() nr.nrNum = 'NR 0000002' nr.stateCd = State.DRAFT nr.requestId = 1460775 nr._source = 'NRO' name1 = NameDAO() name1.choice = 1 name1.name = 'TEST NAME ONE' nr.names = [name1] nr.save_to_db() EventRecorder.record(user, Event.POST, nr, nr.json()) nr.stateCd = State.INPROGRESS nr.save_to_db() EventRecorder.record(user, Event.PUT, nr, {"additional": "additional","furnished": "N"}) # get the resource (this is the test) rv = client.get('/api/v1/events/NR%200000002', headers=headers) assert rv.status_code == 200 assert b'"user_action": "Edit NR Details (NameX)"' in rv.data
def complete_reservation_payment(self, nr_model: RequestDAO, payment_id: int): """ Invoked when completing an in-progress Name Request reservation. :param nr_model: :param payment_id: :return: """ nr_svc = self.nr_service # Update the state of the payment payment = get_active_payment(nr_model, payment_id) sbc_payment_response = get_payment(payment.payment_token) # TODO: Throw errors if this fails! if sbc_payment_response.statusCode in [ PaymentStatusCode.COMPLETED.value, PaymentStatusCode.APPROVED.value ]: payment.payment_status_code = sbc_payment_response.statusCode payment.payment_completion_date = sbc_payment_response.createdOn payment.save_to_db() # This handles updates if the NR state is DRAFT, COND_RESERVE or RESERVED # If the state is COND_RESERVE update state to CONDITIONAL # If the state is RESERVED update state to APPROVED # Then update the name request as required if nr_model.stateCd == State.DRAFT: # If the state is DRAFT, leave it as a DRAFT nr_model = self.update_nr(nr_model, State.DRAFT, self.handle_nr_approve) if nr_model.stateCd == State.COND_RESERVE: # If the state is COND_RESERVE update state to CONDITIONAL, and update the name request as required nr_model = self.update_nr(nr_model, State.CONDITIONAL, self.handle_nr_approve) elif nr_model.stateCd == State.RESERVED: # If the state is RESERVED update state to APPROVED, and update the name request as required nr_model = self.update_nr(nr_model, State.APPROVED, self.handle_nr_approve) # Save the name request nr_model.save_to_db() # Record the event EventRecorder.record(nr_svc.user, Event.PATCH + ' [payment completed] RESERVE', nr_model, nr_model.json()) # Update the actions, as things change once the payment is successful self.nr_service.current_state_actions = get_nr_state_actions( nr_model.stateCd, nr_model) return nr_model
def complete_reapply_payment(self, nr_model: RequestDAO, payment_id: int): """ Invoked when re-applying for an existing Name Request reservation. Extend the Name Request's expiration date by 56 days. If the request action is set to REH, REN or REST, OR request type is 'RCR', 'RUL', 'BERE', 'RCC', 'RCP', 'RFI', 'XRCR', 'RLC', 'XRCP','RSO','XRSO' extend the expiration by an additional year (plus the default 56 days). :param nr_model: :param payment_id: :return: """ nr_svc = self.nr_service # Update the state of the payment payment = get_active_payment(nr_model, payment_id) sbc_payment_response = get_payment(payment.payment_token) # TODO: Throw errors if this fails! if sbc_payment_response.statusCode in [ PaymentStatusCode.COMPLETED.value, PaymentStatusCode.APPROVED.value ]: payment.payment_status_code = sbc_payment_response.statusCode payment.payment_completion_date = sbc_payment_response.createdOn payment.save_to_db() if nr_model.submitCount < 3: nr_model = nr_svc.extend_expiry_date(nr_model, datetime.utcnow()) nr_model = nr_svc.update_request_submit_count(nr_model) nr_model.save_to_db() else: # TODO: Make a custom exception for this? raise PaymentServiceError( message= 'Submit count maximum of 3 retries has been reached!') # This (optionally) handles the updates for NRO and Solr, if necessary update_solr = False nr_model = self.update_records_in_network_services( nr_model, update_solr) # Update the actions, as things change once the payment is successful self.nr_service.current_state_actions = get_nr_state_actions( nr_model.stateCd, nr_model) # Record the event EventRecorder.record(nr_svc.user, Event.PATCH + ' [payment completed] REAPPLY', nr_model, nr_model.json()) return nr_model
def handle_patch_request_refund(self, nr_model: Request): """ Can the NR and request a refund for ALL associated Name Request payments. :param nr_model: :return: """ nr_svc = self.nr_service # This handles updates if the NR state is 'patchable' nr_model = self.update_nr(nr_model, State.REFUND_REQUESTED, self.handle_nr_patch) # Handle the payments valid_states = [ PaymentState.APPROVED.value, PaymentState.COMPLETED.value, PaymentState.PARTIAL.value ] refund_value = 0 # Check for NR that has been renewed - do not refund any payments. # UI should not order refund for an NR renewed/reapplied it. if not any( payment.payment_action == Payment.PaymentActions.REAPPLY.value for payment in nr_model.payments.all()): # Try to refund all payments associated with the NR for payment in nr_model.payments.all(): if payment.payment_status_code in valid_states: # Some refunds may fail. Some payment methods are not refundable and return HTTP 400 at the refund. # The refund status is checked from the payment_response and a appropriate message is displayed by the UI. refund_payment(payment.payment_token, {}) payment_response = get_payment(payment.payment_token) payment.payment_status_code = PaymentState.REFUND_REQUESTED.value payment.save_to_db() refund_value += payment_response.receipts[0][ 'receiptAmount'] if len( payment_response.receipts) else 0 publish_email_notification(nr_model.nrNum, 'refund', '{:.2f}'.format(refund_value)) # This handles the updates for NRO and Solr, if necessary nr_model = self.update_records_in_network_services(nr_model, update_solr=True) # Record the event EventRecorder.record(nr_svc.user, Event.PATCH + ' [request-refund]', nr_model, nr_model.json()) return nr_model
def complete_upgrade_payment(self, nr_model: RequestDAO, payment_id: int): """ Invoked when upgrading an existing Name Request reservation to PRIORITY status. :param nr_model: :param payment_id: :return: """ nr_svc = self.nr_service if nr_model.stateCd not in [State.DRAFT, State.PENDING_PAYMENT]: raise PaymentServiceError( message= 'Error upgrading Name Request, request is in an invalid state!' ) # Update the state of the payment payment = get_active_payment(nr_model, payment_id) sbc_payment_response = get_payment(payment.payment_token) # TODO: Throw errors if this fails! if sbc_payment_response.statusCode in [ PaymentStatusCode.COMPLETED.value, PaymentStatusCode.APPROVED.value ]: payment.payment_status_code = sbc_payment_response.statusCode payment.payment_completion_date = sbc_payment_response.createdOn payment.save_to_db() nr_model.priorityCd = 'Y' nr_model.priorityDate = datetime.utcnow() # Save the name request nr_model.save_to_db() # This (optionally) handles the updates for NRO and Solr, if necessary update_solr = False nr_model = self.update_records_in_network_services( nr_model, update_solr) # Update the actions, as things change once the payment is successful self.nr_service.current_state_actions = get_nr_state_actions( nr_model.stateCd, nr_model) # Record the event EventRecorder.record(nr_svc.user, Event.PATCH + ' [payment completed] UPGRADE', nr_model, nr_model.json()) return nr_model
def test_update_nr_header_with_mutating_priority(freeze_datetime_utcnow, test_name, initial_priority_cd, second_priority_code, expected_cd, expected_dt): from namex.services.nro.request_utils import add_nr_header nr = Request() user = User('idir/bob', 'bob', 'last', 'idir', 'localhost') nr_submitter = { 'submitted_date': EPOCH_DATETIME, 'submitter': 'doesnt matter' } nr_header = { 'priority_cd': initial_priority_cd, 'state_type_cd': 'H', 'nr_num': 'NR 0000001', 'request_id': 1, 'previous_request_id': None, 'submit_count': 0, 'request_type_cd': 'REQ', 'expiration_date': None, 'additional_info': None, 'nature_business_info': 'N/A', 'xpro_jurisdiction': None, 'submitted_date': EPOCH_DATETIME, 'last_update': EPOCH_DATETIME } print(nr.json()) add_nr_header(nr, nr_header, nr_submitter, user) nr_header['priority_cd'] = second_priority_code add_nr_header(nr, nr_header, nr_submitter, user) assert expected_cd == nr.priorityCd assert expected_dt == nr.priorityDate
def handle_patch_request_refund(self, nr_model: Request): """ Can the NR and request a refund for ALL associated Name Request payments. :param nr_model: :return: """ nr_svc = self.nr_service # This handles updates if the NR state is 'patchable' nr_model = self.update_nr(nr_model, State.REFUND_REQUESTED, self.handle_nr_patch) # Handle the payments valid_states = [ PaymentState.APPROVED.value, PaymentState.COMPLETED.value, PaymentState.PARTIAL.value ] # Cancel any payments associated with the NR for payment in nr_model.payments.all(): if payment.payment_status_code in valid_states: if NameRequestFields._should_refund_sbc_payment( payment.payment_token): # refund_payment(payment.payment_token, {'reason': 'Name Request user requested refund'}) refund_payment(payment.payment_token, {}) payment.payment_status_code = PaymentState.REFUND_REQUESTED.value payment.save_to_db() # This handles the updates for NRO and Solr, if necessary nr_model = self.update_records_in_network_services(nr_model, update_solr=True) # Record the event EventRecorder.record(nr_svc.user, Event.PATCH + ' [request-refund]', nr_model, nr_model.json()) return nr_model
def cancel_payment(self, nr_model: RequestDAO, payment_id: int): # Cancel payment with specified id. valid_states = [PaymentState.CREATED.value] for payment in nr_model.payments.all(): if payment.id == payment_id and payment.payment_status_code in valid_states: sbc_payment_response = get_payment(payment.payment_token) if sbc_payment_response.statusCode in [ PaymentStatusCode.COMPLETED.value, PaymentStatusCode.APPROVED.value ]: raise PaymentServiceError( message= 'Error cancelling payment. Payment is in a completed state!' ) cancel_payment(payment.payment_token) payment.payment_status_code = PaymentState.CANCELLED.value payment.save_to_db() # record the event nr_svc = self.nr_service EventRecorder.record( nr_svc.user, Event.DELETE + f' [payment cancelled] {payment.payment_action}', nr_model, nr_model.json()) return nr_model
def post(*args, **kwargs): app_config = current_app.config.get('SOLR_SYNONYMS_API_URL', None) if not app_config: current_app.logger.error('ENV is not set') return None, 'Internal server error', 500 test_env = 'prod' if test_env in app_config: current_app.logger.info( 'Someone is trying to post a new request. Not available.') return jsonify( {'message': 'Not Implemented in the current environment'}), 501 json_data = request.get_json() if not json_data: current_app.logger.error("Error when getting json input") return jsonify({'message': 'No input data provided'}), 400 try: restricted = VirtualWordConditionService() except Exception as error: current_app.logger.error( "'Error initializing VirtualWordCondition Service: Error:{0}". format(error)) return jsonify({"message": "Virtual Word Condition Service error"}), 404 try: user = User.find_by_username('name_request_service_account') user_id = user.id except Exception as error: current_app.logger.error( "Error getting user id: Error:{0}".format(error)) return jsonify({"message": "Get User Error"}), 404 try: name_request = Request() except Exception as error: current_app.logger.error( "Error initializing name_request object Error:{0}".format( error)) return jsonify({"message": "Name Request object error"}), 404 try: nr_num = generate_nr() nr_id = get_request_sequence() except Exception as error: current_app.logger.error( "Error getting nr number. Error:{0}".format(error)) return jsonify({"message": "Error getting nr number"}), 404 #set the request attributes try: name_request.id = nr_id name_request.submittedDate = datetime.utcnow() name_request.requestTypeCd = set_request_type( json_data['entity_type'], json_data['request_action']) name_request.nrNum = nr_num except Exception as error: current_app.logger.error( "Error setting request header attributes. Error:{0}".format( error)) return jsonify( {"message": "Error setting request header attributes"}), 404 try: if (json_data['stateCd'] == 'COND-RESERVE'): name_request.consentFlag = 'Y' if json_data['stateCd'] in [State.RESERVED, State.COND_RESERVE]: name_request.expirationDate = create_expiry_date( start=name_request.submittedDate, expires_in_days=56, tz=timezone('UTC')) name_request.stateCd = json_data['stateCd'] name_request.entity_type_cd = json_data['entity_type'] name_request.request_action_cd = json_data['request_action'] except Exception as error: current_app.logger.error( "Error setting reserve state and expiration date. Error:{0}". format(error)) return jsonify( {"message": "Error setting reserve state and expiration date"}), 404 #set this to name_request_service_account name_request.userId = user_id try: lang_comment = add_language_comment(json_data['english'], user_id, nr_id) name_request.comments.append(lang_comment) except Exception as error: current_app.logger.error( "Error setting language comment. Error:{0}".format(error)) return jsonify({"message": "Error setting language comment."}), 404 try: if json_data['nameFlag'] == True: name_comment = add_name_comment(user_id, nr_id) name_request.comments.append(name_comment) except Exception as error: current_app.logger.error( "Error setting person name comment. Error:{0}".format(error)) return jsonify({"message": "Error setting person name comment."}), 404 try: if json_data['submit_count'] is None: name_request.submitCount = 1 else: name_request.submitCount = +1 except Exception as error: current_app.logger.error( "Error setting submit count. Error:{0}".format(error)) return jsonify({"message": "Error setting submit count."}), 404 try: if json_data['stateCd'] == State.DRAFT: # set request header attributes name_request = set_draft_attributes(name_request, json_data, user_id) name_request.save_to_db() nrd = Request.find_by_nr(name_request.nrNum) try: # set applicant attributes nrd_app = set_applicant_attributes(json_data, nr_id) nrd.applicants.append(nrd_app) except Exception as error: current_app.logger.error( "Error setting applicant. Error:{0}".format(error)) return jsonify({"message": "Error setting applicant."}), 404 except Exception as error: current_app.logger.error( "Error setting DRAFT attributes. Error:{0}".format(error)) return jsonify({"message": "Error setting DRAFT attributes."}), 404 try: if json_data['stateCd'] in [ State.RESERVED, State.COND_RESERVE, State.DRAFT ]: name_request.save_to_db() nrd = Request.find_by_nr(name_request.nrNum) except Exception as error: current_app.logger.error( "Error saving reservation to db. Error:{0}".format(error)) return jsonify({"message": "Error saving reservation to db."}), 404 try: nrd = Request.find_by_nr(name_request.nrNum) except Exception as error: current_app.logger.error( "Error retrieving the New NR from the db. Error:{0}".format( error)) return jsonify( {"message": "Error retrieving the New NR from the db."}), 404 try: for name in json_data.get('names', None): try: submitted_name = Name() name_id = get_name_sequence() submitted_name.id = name_id except Exception as error: current_app.logger.error( "Error on submitted Name object. Error:{0}".format( error)) return jsonify( {"message": "Error on submitted_name and sequence."}), 404 #common name attributes try: submitted_name.choice = name['choice'] submitted_name.name = name['name'] if (name['name_type_cd']): submitted_name.name_type_cd = name['name_type_cd'] else: submitted_name.name_type_cd = 'CO' if (json_data['stateCd'] == State.DRAFT): submitted_name.state = 'NE' else: submitted_name.state = json_data['stateCd'] if name['designation']: submitted_name.designation = name['designation'] submitted_name.nrId = nr_id except Exception as error: current_app.logger.error( "Error on common name attributes. Error:{0}".format( error)) return jsonify( {"message": "Error on common name attributes."}), 404 decision_text = None if json_data['stateCd'] in [ State.RESERVED, State.COND_RESERVE ]: try: # only capturing one conflict if (name['conflict1_num']): submitted_name.conflict1_num = name[ 'conflict1_num'] if name['conflict1']: submitted_name.conflict1 = name['conflict1'] #conflict text same as Namex decision_text = 'Consent is required from ' + name[ 'conflict1'] + '\n' + '\n' except Exception as error: current_app.logger.error( "Error on reserved conflict info. Error:{0}". format(error)) return jsonify( {"message": "Error on reserved conflict info."}), 404 else: try: submitted_name.conflict1_num = None submitted_name.conflict1 = None except Exception as error: current_app.logger.error( "Error on draft empty conflict info. Error:{0}". format(error)) return jsonify( {"message": "Error on draft empty conflict info."}), 404 consent_list = name['consent_words'] if len(consent_list) > 0: for consent in consent_list: try: cnd_instructions = None if consent != "" or len(consent) > 0: cnd_instructions = restricted.get_word_condition_instructions( consent) except Exception as error: current_app.logger.error( "Error on get consent word. Consent Word[0], Error:{1}" .format(consent, error)) return jsonify( {"message": "Error on get consent words."}), 404 try: if (decision_text is None): decision_text = cnd_instructions + '\n' else: decision_text += consent + '- ' + cnd_instructions + '\n' submitted_name.decision_text = decision_text except Exception as error: current_app.logger.error( "Error on adding consent words to decision. Error:{0}" .format(error)) return jsonify({ "message": "Error on adding consent words to decision text" }), 404 try: nrd.names.append(submitted_name) except Exception as error: current_app.logger.error( "Error appending names. Error:{0}".format(error)) return jsonify({"message": "Error appending names"}), 404 try: #save names nrd.save_to_db() except Exception as error: current_app.logger.error( "Error saving the whole nr and names. Error:{0}".format( error)) return jsonify({"message": "Error saving names to the db"}), 404 except Exception as error: current_app.logger.error( "Error setting name. Error:{0}".format(error)) return jsonify({"message": "Error setting name."}), 404 #TODO: Need to add verification that the save was successful. #update solr for reservation try: if (json_data['stateCd'] in ['RESERVED', 'COND-RESERVE']): solr_name = nrd.names[0].name solr_docs = [] nr_doc = { "id": name_request.nrNum, "name": solr_name, "source": "NR", "start_date": name_request.submittedDate.strftime("%Y-%m-%dT%H:%M:00Z") } solr_docs.append(nr_doc) update_solr('possible.conflicts', solr_docs) except Exception as error: current_app.logger.error( "Error updating solr for reservation. Error:{0}".format(error)) return jsonify({"message": "Error updating solr for reservation."}), 404 current_app.logger.debug(name_request.json()) return jsonify(name_request.json()), 200