def consume_nr(
        business: Business,
        filing: Filing,
        nr_num_path='/filing/incorporationApplication/nameRequest/nrNumber'):
    """Update the nr to a consumed state."""
    try:
        # skip this if none (nrNumber will not be available for numbered company)
        if nr_num := get_str(filing.filing_json, nr_num_path):

            namex_svc_url = current_app.config.get('NAMEX_API')
            token = AccountService.get_bearer_token()

            # Create an entity record
            data = json.dumps({'consume': {'corpNum': business.identifier}})
            rv = requests.patch(
                url=''.join([namex_svc_url, nr_num]),
                headers={
                    **AccountService.CONTENT_TYPE_JSON, 'Authorization':
                    AccountService.BEARER + token
                },
                data=data,
                timeout=AccountService.timeout)
            if not rv.status_code == HTTPStatus.OK:
                raise QueueException

            # remove the NR from the account
            if filing.temp_reg and (bootstrap :=
                                    RegistrationBootstrap.find_by_identifier(
                                        filing.temp_reg)):
                AccountService.delete_affiliation(bootstrap.account, nr_num)
Exemple #2
0
def test_get_temp_business_filing(session, client, jwt):
    """Assert that the business info cannot be received in a valid JSONSchema format."""
    #
    # setup
    identifier = 'Tb31yQIuBw'
    filing_name = 'incorporationApplication'
    temp_reg = RegistrationBootstrap()
    temp_reg._identifier = identifier
    temp_reg.save()
    json_data = copy.deepcopy(FILING_HEADER)
    json_data['filing']['header']['name'] = filing_name
    json_data['filing'][filing_name] = copy.deepcopy(INCORPORATION)
    filings = factory_pending_filing(None, json_data)
    filings.temp_reg = identifier
    filings.save()

    #
    # test
    rv = client.get(f'/api/v2/businesses/{identifier}/filings',
                    headers=create_header(jwt, [STAFF_ROLE], identifier))

    #
    # validate
    assert rv.status_code == HTTPStatus.OK
    assert rv.json['filing']['header']['name'] == filing_name
    assert rv.json['filing'][filing_name] == INCORPORATION
Exemple #3
0
def test_create_bootstrap_registrations(session):
    """Assert the service creates registrations."""
    identifier_base = 'Tabc123'

    for i in range(5):
        r = RegistrationBootstrap(identifier=identifier_base + str(i),
                                  account=12)
        r.save()
    assert r.identifier == identifier_base + str(4)
Exemple #4
0
def consume_nr(
        business: Business,
        filing: Filing,
        nr_num_path='/filing/incorporationApplication/nameRequest/nrNumber'):
    """Update the nr to a consumed state."""
    try:
        nr_num = get_str(filing.filing_json, nr_num_path)
        # skip this if none (nrNumber will not be available for numbered company)
        if nr_num:
            bootstrap = RegistrationBootstrap.find_by_identifier(
                filing.temp_reg)
            namex_svc_url = current_app.config.get('NAMEX_API')
            token = AccountService.get_bearer_token()

            # Create an entity record
            data = json.dumps({'consume': {'corpNum': business.identifier}})
            rv = requests.patch(
                url=''.join([namex_svc_url, nr_num]),
                headers={
                    **AccountService.CONTENT_TYPE_JSON, 'Authorization':
                    AccountService.BEARER + token
                },
                data=data,
                timeout=AccountService.timeout)
            if not rv.status_code == HTTPStatus.OK:
                raise QueueException

            # remove the NR from the account
            AccountService.delete_affiliation(bootstrap.account, nr_num)
    except KeyError:
        pass  # return
    except Exception:  # pylint: disable=broad-except; note out any exception, but don't fail the call
        sentry_sdk.capture_message(
            f'Queue Error: Consume NR error for filing:{filing.id}',
            level='error')
Exemple #5
0
def test_only_one_registration_bootstrap(session):
    """Assert that the identifier cannot be used more than once."""
    identifier = 'Tabc123'

    r = RegistrationBootstrap(identifier=identifier, account=12)
    r.save()

    with pytest.raises(FlushError):
        p = RegistrationBootstrap(identifier=identifier, account=12)
        p.save()
Exemple #6
0
    def create_bootstrap(account: int) -> Union[Dict, RegistrationBootstrap]:
        """Return either a new bootstrap registration or an error struct."""
        if not account:
            return {'error': babel('An account number must be provided.')}

        bootstrap = RegistrationBootstrap(account=account)
        allowed_encoded = string.ascii_letters + string.digits

        # try to create a bootstrap registration with a unique ID
        for _ in range(5):
            bootstrap.identifier = 'T' + ''.join(secrets.choice(allowed_encoded) for _ in range(9))
            try:
                bootstrap.save()
                return bootstrap
            except FlushError:
                pass  # we try again
            except Exception:
                break

        return {'error': babel('Unable to create bootstrap registration.')}
Exemple #7
0
def test_delete_bootstrap_draft_filing(client, jwt, session):
    """Assert that a draft IA filing can be retrieved."""
    account_id = 26
    identifier, filing_id = setup_bootstrap_ia_minimal(jwt, session, client,
                                                       account_id)
    #
    # Test that we can get the filing
    #
    rv = client.delete(f'/api/v1/businesses/{identifier}/filings/{filing_id}',
                       headers=create_header(jwt, [STAFF_ROLE], None))

    assert rv.status_code == HTTPStatus.OK
    assert not Filing.find_by_id(filing_id)
    assert not RegistrationBootstrap.find_by_identifier(identifier)
Exemple #8
0
    def delete(identifier, filing_id=None):  # pylint: disable=too-many-branches
        """Delete a filing from the business."""
        if not filing_id:
            return ({
                'message': _('No filing id provided for:') + identifier
            }, HTTPStatus.BAD_REQUEST)

        # check authorization
        if not authorized(identifier, jwt, action=['edit']):
            return jsonify({'message':
                            _('You are not authorized to delete a filing for:') + identifier}),\
                HTTPStatus.UNAUTHORIZED

        if identifier.startswith('T'):
            filing = Filing.get_temp_reg_filing(identifier, filing_id)
        else:
            filing = Business.get_filing_by_id(identifier, filing_id)

        if not filing:
            return jsonify({'message':
                            _('Filing Not Found.')}), HTTPStatus.NOT_FOUND

        if filing.deletion_locked:  # should not be deleted
            return ListFilingResource._create_deletion_locked_response(
                identifier, filing)

        try:
            ListFilingResource._delete_from_minio(filing)
            filing.delete()
        except BusinessException as err:
            return jsonify({'errors': [
                {
                    'error': err.error
                },
            ]}), err.status_code

        if identifier.startswith('T'):
            bootstrap = RegistrationBootstrap.find_by_identifier(identifier)
            if bootstrap:
                deregister_status = RegistrationBootstrapService.deregister_bootstrap(
                    bootstrap)
                delete_status = RegistrationBootstrapService.delete_bootstrap(
                    bootstrap)
                if deregister_status != HTTPStatus.OK or delete_status != HTTPStatus.OK:
                    current_app.logger.error(
                        'Unable to deregister and delete temp reg:',
                        identifier)

        return jsonify({'message': _('Filing deleted.')}), HTTPStatus.OK
Exemple #9
0
def update_affiliation(business: Business, filing: Filing):
    """Create an affiliation for the business and remove the bootstrap."""
    try:
        bootstrap = RegistrationBootstrap.find_by_identifier(filing.temp_reg)

        rv = AccountService.create_affiliation(
            account=bootstrap.account,
            business_registration=business.identifier,
            business_name=business.legal_name,
            corp_type_code=business.legal_type)

        if rv not in (HTTPStatus.OK, HTTPStatus.CREATED):
            deaffiliation = AccountService.delete_affiliation(
                bootstrap.account, business.identifier)
            sentry_sdk.capture_message(
                f'Queue Error: Unable to affiliate business:{business.identifier} for filing:{filing.id}',
                level='error')
        else:
            # flip the registration
            # recreate the bootstrap, but point to the new business in the name
            old_bs_affiliation = AccountService.delete_affiliation(
                bootstrap.account, bootstrap.identifier)
            new_bs_affiliation = AccountService.create_affiliation(
                account=bootstrap.account,
                business_registration=bootstrap.identifier,
                business_name=business.identifier,
                corp_type_code='TMP')
            reaffiliate = bool(
                new_bs_affiliation in (HTTPStatus.OK, HTTPStatus.CREATED)
                and old_bs_affiliation == HTTPStatus.OK)

        if rv not in (HTTPStatus.OK, HTTPStatus.CREATED) \
                or ('deaffiliation' in locals() and deaffiliation != HTTPStatus.OK)\
                or ('reaffiliate' in locals() and not reaffiliate):
            raise QueueException
    except Exception as err:  # pylint: disable=broad-except; note out any exception, but don't fail the call
        sentry_sdk.capture_message(
            f'Queue Error: Affiliation error for filing:{filing.id}, with err:{err}',
            level='error')
Exemple #10
0
 def delete_bootstrap(bootstrap: RegistrationBootstrap):
     """Delete the bootstrap registration."""
     with contextlib.suppress(Exception):
         bootstrap.delete()
     return HTTPStatus.OK
Exemple #11
0
    def _save_filing(
        client_request: LocalProxy,  # pylint: disable=too-many-return-statements,too-many-branches
        business_identifier: str,
        user: User,
        filing_id: int
    ) -> Tuple[Union[Business, RegistrationBootstrap], Filing, dict, int]:
        """Save the filing to the ledger.

        If not successful, a dict of errors is returned.

        Returns: {
            Business: business model object found for the identifier provided
            Filing: filing model object for the submitted filing
            dict: a dict of errors
            int: the HTTPStatus error code

        @TODO refactor to a set of single putpose routines
        }
        """
        json_input = client_request.get_json()
        if not json_input:
            return None, None, {'message':
                                f'No filing json data in body of post for {business_identifier}.'}, \
                HTTPStatus.BAD_REQUEST

        if business_identifier.startswith('T'):
            # bootstrap filing
            bootstrap = RegistrationBootstrap.find_by_identifier(
                business_identifier)
            business = None
            if not bootstrap:
                return None, None, {
                    'message': f'{business_identifier} not found'
                }, HTTPStatus.NOT_FOUND
            if client_request.method == 'PUT':
                rv = db.session.query(Filing). \
                    filter(Filing.temp_reg == business_identifier). \
                    filter(Filing.id == filing_id). \
                    one_or_none()
                if not rv:
                    return None, None, {
                        'message': f'{business_identifier} no filings found'
                    }, HTTPStatus.NOT_FOUND
                filing = rv
            else:
                filing = Filing()
                filing.temp_reg = bootstrap.identifier
                if not json_input['filing'].get('business'):
                    json_input['filing']['business'] = {}
                json_input['filing']['business'][
                    'identifier'] = bootstrap.identifier

        else:
            # regular filing for a business
            business = Business.find_by_identifier(business_identifier)
            if not business:
                return None, None, {
                    'message': f'{business_identifier} not found'
                }, HTTPStatus.NOT_FOUND

            if client_request.method == 'PUT':
                rv = db.session.query(Business, Filing). \
                    filter(Business.id == Filing.business_id). \
                    filter(Business.identifier == business_identifier). \
                    filter(Filing.id == filing_id). \
                    one_or_none()
                if not rv:
                    return None, None, {
                        'message': f'{business_identifier} no filings found'
                    }, HTTPStatus.NOT_FOUND
                filing = rv[1]
            else:
                filing = Filing()
                filing.business_id = business.id

        try:
            filing.submitter_id = user.id
            filing.filing_json = json_input
            filing.source = filing.filing_json['filing']['header'].get(
                'source', Filing.Source.LEAR.value)
            if filing.source == Filing.Source.COLIN.value:
                try:
                    filing.filing_date = datetime.datetime.fromisoformat(
                        filing.filing_json['filing']['header']['date'])
                    for colin_id in filing.filing_json['filing']['header'][
                            'colinIds']:
                        colin_event_id = ColinEventId()
                        colin_event_id.colin_event_id = colin_id
                        filing.colin_event_ids.append(colin_event_id)
                except KeyError:
                    current_app.logger.error(
                        'Business:%s missing filing/header values, unable to save',
                        business.identifier)
                    return None, None, {
                        'message': 'missing filing/header values'
                    }, HTTPStatus.BAD_REQUEST
            else:
                filing.filing_date = datetime.datetime.utcnow()

            # for any legal type, set effective date as set in json; otherwise leave as default
            filing.effective_date = \
                datetime.datetime.fromisoformat(filing.filing_json['filing']['header']['effectiveDate']) \
                if filing.filing_json['filing']['header'].get('effectiveDate', None) else datetime.datetime.utcnow()

            filing.save()
        except BusinessException as err:
            return None, None, {'error': err.error}, err.status_code

        return business or bootstrap, filing, None, None
Exemple #12
0
    def put(identifier, filing_id):  # pylint: disable=too-many-return-statements,too-many-locals
        """Modify an incomplete filing for the business."""
        # basic checks
        err_msg, err_code = ListFilingResource._put_basic_checks(
            identifier, filing_id, request)
        if err_msg:
            return jsonify({'errors': [
                err_msg,
            ]}), err_code
        json_input = request.get_json()

        # check authorization
        response, response_code = ListFilingResource._check_authorization(
            identifier, json_input)
        if response:
            return response, response_code

        # get query params
        draft = (request.args.get('draft', None).lower() == 'true') \
            if request.args.get('draft', None) else False
        only_validate = (request.args.get('only_validate', None).lower() == 'true') \
            if request.args.get('only_validate', None) else False

        # get header params
        payment_account_id = request.headers.get('accountId', None)

        if not draft \
                and not ListFilingResource._is_historical_colin_filing(json_input) \
                and not ListFilingResource._is_before_epoch_filing(json_input, Business.find_by_identifier(identifier)):
            if identifier.startswith('T'):
                business_validate = RegistrationBootstrap.find_by_identifier(
                    identifier)
            else:
                business_validate = Business.find_by_identifier(identifier)
            err = validate(business_validate, json_input)
            # err_msg, err_code = ListFilingResource._validate_filing_json(request)
            if err or only_validate:
                if err:
                    json_input['errors'] = err.msg
                    return jsonify(json_input), err.code
                return jsonify(json_input), HTTPStatus.OK

        # save filing, if it's draft only then bail
        user = User.get_or_create_user_by_jwt(g.jwt_oidc_token_info)
        try:
            business, filing, err_msg, err_code = ListFilingResource._save_filing(
                request, identifier, user, filing_id)
            if err_msg or draft:
                reply = filing.json if filing else json_input
                reply['errors'] = [
                    err_msg,
                ]
                return jsonify(reply), err_code or \
                    (HTTPStatus.CREATED if (request.method == 'POST') else HTTPStatus.ACCEPTED)
        except Exception as err:
            print(err)

        # complete filing
        response, response_code = ListFilingResource.complete_filing(
            business, filing, draft, payment_account_id)
        if response and (response_code != HTTPStatus.CREATED
                         or filing.source == Filing.Source.COLIN.value):
            return response, response_code

        # all done
        filing_json = filing.json
        if response:
            filing_json['filing']['header'].update(response)
        return jsonify(filing_json),\
            (HTTPStatus.CREATED if (request.method == 'POST') else HTTPStatus.ACCEPTED)
Exemple #13
0
    def put(identifier, filing_id):  # pylint: disable=too-many-return-statements
        """Modify an incomplete filing for the business."""
        # basic checks
        err_msg, err_code = ListFilingResource._put_basic_checks(
            identifier, filing_id, request)
        if err_msg:
            return jsonify({'errors': [
                err_msg,
            ]}), err_code
        json_input = request.get_json()

        # check authorization
        if not authorized(identifier, jwt, action=['edit']):
            return jsonify({'message':
                            f'You are not authorized to submit a filing for {identifier}.'}), \
                HTTPStatus.UNAUTHORIZED

        # get query params
        draft = (request.args.get('draft', None).lower() == 'true') \
            if request.args.get('draft', None) else False
        only_validate = (request.args.get('only_validate', None).lower() == 'true') \
            if request.args.get('only_validate', None) else False

        # validate filing
        if not draft and not ListFilingResource._is_before_epoch_filing(
                json_input, Business.find_by_identifier(identifier)):
            if identifier.startswith('T'):
                business_validate = RegistrationBootstrap.find_by_identifier(
                    identifier)
            else:
                business_validate = Business.find_by_identifier(identifier)
            err = validate(business_validate, json_input)
            # err_msg, err_code = ListFilingResource._validate_filing_json(request)
            if err or only_validate:
                if err:
                    json_input['errors'] = err.msg
                    return jsonify(json_input), err.code
                return jsonify(json_input), HTTPStatus.OK

        # save filing, if it's draft only then bail
        user = User.get_or_create_user_by_jwt(g.jwt_oidc_token_info)
        try:
            business, filing, err_msg, err_code = ListFilingResource._save_filing(
                request, identifier, user, filing_id)
            if err_msg or draft:
                reply = filing.json if filing else json_input
                reply['errors'] = [
                    err_msg,
                ]
                return jsonify(reply), err_code or \
                    (HTTPStatus.CREATED if (request.method == 'POST') else HTTPStatus.ACCEPTED)
        except Exception as err:
            print(err)

        # complete filing
        response, response_code = ListFilingResource.complete_filing(
            business, filing, draft)
        if response:
            return response, response_code

        # all done
        return jsonify(filing.json),\
            (HTTPStatus.CREATED if (request.method == 'POST') else HTTPStatus.ACCEPTED)