"""Contract Mechanical Deduction handlers.""" from datetime import datetime from http import HTTPStatus from abacus_common_logic.constants.error import ERROR_ENTITY_DOES_NOT_EXIST from abacus_common_logic.utils.authorization import permissions_authorize_many_accounts from abacus_common_logic.views.create_view import CreateView from abacus_common_logic.views.item_view import ItemView from abacus_common_logic.views.validations import validated_request_body from common_apispec import doc, marshal_with from flask import Blueprint, g, request from owsrequest import flask_request from owsresponse import response from owsresponse.adaptors.flask import flaskify from abacus_contract.constants.error import ( ERROR_CODE_AUTHORIZATION, ERROR_CODE_FORBIDDEN, ERROR_INVALID_DATE, ERROR_MESSAGE_FORBIDDEN_USER, ) from abacus_contract.logic import contract_mechanical_deduction as logic from abacus_contract.models import ContractMechanicalDeduction from abacus_contract.schemas.contract_mechanical_deduction import ( ContractMechanicalDeductionDetailSchema, ContractMechanicalDeductionPostSchema, ContractMechanicalDeductionPutSchema, ContractWorldWideMechanicalDeductionPostSchema, MechadminResponseSchema, ) from abacus_contract.utils.authorization import pdp_authorize_many_accounts from abacus_contract.utils.format_error import validation_error from core.config import ows_client contract_mechanical_deduction_api = Blueprint( 'contract_mechanical_deduction_api', __name__ ) class ContractMechanicalDeductionItemView(ItemView): """View for finding contract_mechanical_deduction by ID.""" model_class = ContractMechanicalDeduction put_schema = ContractMechanicalDeductionPutSchema() object_detail_schema = ContractMechanicalDeductionDetailSchema() def get(self, object_id): """Find object by its id or return an error.""" access_rule_decision = flask_request.verify_rules_access_standalone(request) if not access_rule_decision: return flaskify( response.create_error_response( code=ERROR_CODE_AUTHORIZATION, message='Unauthorized', status=401, ) ) obj = self.model_class.get_by_id(object_id) if not obj or obj.deleted_at is not None or obj.deleted_by is not None: return flaskify( response.create_not_found_response( message=ERROR_ENTITY_DOES_NOT_EXIST.format( object_type='Contract Mechanical Deduction', object_id=object_id ) ) ) body = self.object_detail_schema.dump(obj) return flaskify(response.Response(message=body, status=200)) def put(self, object_id, **kwargs): """Update contract_mechanical_deductrion by id.""" access_rule_decision = flask_request.verify_rules_access_standalone(request) if not access_rule_decision: return flaskify( response.create_error_response( code=ERROR_CODE_AUTHORIZATION, message='Unauthorized', status=401, ) ) return super().put(object_id, **kwargs) def update_handler(self, obj, **params): """Handle contract_mechanical_deduction updates.""" return logic.update_contract_mechanical_deduction(obj, **params) def delete(self, object_id): """Delete a contract_mechanical_deduction.""" access_rule_decision = flask_request.verify_rules_access_standalone(request) if not access_rule_decision: return flaskify( response.create_error_response( code=ERROR_CODE_AUTHORIZATION, message='Unauthorized', status=401, ) ) obj = self.model_class.get_by_id_or_error(object_id, 404) return flaskify(logic.soft_delete_contract_mechanical_deduction(obj)) class ContractMechanicalDeductionCreateView(CreateView): """Handles contract_mechanical_deduction creation.""" post_schema = ContractMechanicalDeductionPostSchema() def post(self, **kwargs): """Create a contract_mechanical_deduction.""" access_rule_decision = flask_request.verify_rules_access_standalone(request) if not access_rule_decision: return flaskify( response.create_error_response( code=ERROR_CODE_AUTHORIZATION, message='Unauthorized', status=401, ) ) return super().post(**kwargs) def create_handler(self, **params): """Create a contract_mechanical_deduction.""" return logic.create_contract_mechanical_deduction(**params) contract_mechanical_deduction_api.add_url_rule( '/contract-mechanical-deduction/', methods=['GET', 'PUT', 'DELETE'], view_func=ContractMechanicalDeductionItemView.as_view( 'contract_mechanical_deduction' ), ) contract_mechanical_deduction_api.add_url_rule( '/contract//contract-mechanical-deduction/', view_func=ContractMechanicalDeductionCreateView.as_view( 'create_contract_mechanical_deduction' ), ) @contract_mechanical_deduction_api.route( '/contract//contract-mechanical-deductions/', methods=['GET'] ) def get_contract_mechanical_deductions_by_contract_id(contract_id: int): """Get list of contract mechanical deductions for specified contract_id. Arg: contract_id(int): id of the contract """ (res, contract) = logic.get_contract_mechanical_deductions_by_contract_id( contract_id ) access_rule_decision = flask_request.verify_rules_access_standalone(request) if not access_rule_decision: authorized = pdp_authorize_many_accounts([contract.account_id]) if not authorized: return flaskify( response.create_error_response( code=ERROR_CODE_FORBIDDEN, message=ERROR_MESSAGE_FORBIDDEN_USER, status=403, ) ) account_access = permissions_authorize_many_accounts( ows_client, g.request_context.profile_type, g.request_context.profile_id, [contract.account_id], ) if not account_access: return flaskify( response.create_error_response( code=ERROR_CODE_FORBIDDEN, message=ERROR_MESSAGE_FORBIDDEN_USER, status=403, ) ) return flaskify(res) @contract_mechanical_deduction_api.route( '/contract//contract-mechanical-deductions/worldwide/', methods=['POST'], ) def create_contract_mechanical_deductions_worldwide(contract_id: int): """Create contract mechanical deductions worldwide for specified contract_id. Arg: contract_id(int): id of the contract """ access_rule_decision = flask_request.verify_rules_access_standalone(request) if not access_rule_decision: return flaskify( response.create_error_response( code=ERROR_CODE_AUTHORIZATION, message='Unauthorized', status=401, ) ) post_request_body = validated_request_body( ContractWorldWideMechanicalDeductionPostSchema() ) return flaskify( logic.create_contract_mechanical_deductions_worldwide( contract_id, **post_request_body ) ) @contract_mechanical_deduction_api.route( '/contracts/mechanical-deductions/active', methods=['GET'] ) def get_active_contracts_with_mechanical_deductions(): """Get active contracts with mechanical deductions by date.""" access_rule_decision = flask_request.verify_rules_access_standalone(request) if not access_rule_decision: return flaskify( response.create_error_response( code=ERROR_CODE_AUTHORIZATION, message='Unauthorized', status=401, ) ) date = request.args.get('date') or datetime.now().strftime('%Y-%m-%d') try: date = datetime.strptime(date, '%Y-%m-%d').date() except ValueError: return flaskify(validation_error(ERROR_INVALID_DATE)) return flaskify(logic.get_active_contracts_with_mechanical_deductions(date)) @contract_mechanical_deduction_api.route( '/contracts/mechanical-deductions/account//mechadmin', methods=['GET'], ) @doc( summary='Get whether or not an account is a mechadmin', params={ 'account_id': { 'description': 'The ID of the account', }, }, ) @marshal_with( MechadminResponseSchema(), code=HTTPStatus.OK, description=HTTPStatus.OK.phrase, ) @marshal_with( None, code=HTTPStatus.UNAUTHORIZED, description=HTTPStatus.UNAUTHORIZED.phrase, ) @marshal_with( None, code=HTTPStatus.FORBIDDEN, description=HTTPStatus.FORBIDDEN.phrase, ) def get_mechadmin_for_account(account_id: int): """Get whether or not an account is a mechadmin. The "mechadmin" concept is used in Product Builder. An account is considered to be a mechadmin: - For physical products, if any of its contracts has at least one physical mechanical deduction. - For digital products, if any of its contracts has at least one digital mechanical deduction for the US. """ access_rule_decision = flask_request.verify_rules_access_standalone(request) if not access_rule_decision: return flaskify( response.create_error_response( code=ERROR_CODE_AUTHORIZATION, message='Unauthorized', status=401, ) ) account_access = permissions_authorize_many_accounts( ows_client, g.request_context.profile_type, g.request_context.profile_id, [account_id], ) if not account_access: return flaskify( response.create_error_response( code=ERROR_CODE_FORBIDDEN, message=ERROR_MESSAGE_FORBIDDEN_USER, status=403, ) ) return flaskify(logic.get_mechadmin_for_account(account_id))