accountant/accountant/views/scheduled_operations.py

213 lines
6.3 KiB
Python

"""Module containing scheduled operation related views."""
# vim: set tw=80 ts=4 sw=4 sts=4:
from flask_jwt_extended import jwt_required
from flask_restplus import Namespace, Resource, fields
from sqlalchemy import true
from ..models import db
from ..models.accounts import Account
from ..models.operations import Operation
from ..models.scheduled_operations import ScheduledOperation
# pylint: disable=invalid-name
ns = Namespace(
'scheduled_operation',
description='Scheduled operation management'
)
# Scheduled operation model.
scheduled_operation_model = ns.model('ScheduledOperation', {
'id': fields.Integer(
description='Id of the scheduled operation',
readonly=True,
default=None),
'start_date': fields.Date(
dt_format='iso8601',
required=True,
description='Start date of the scheduled operation'),
'stop_date': fields.Date(
dt_format='iso8601',
required=True,
description='End date of the scheduled operation'),
'day': fields.Integer(
required=True,
description='Day of month for the scheduled operation'),
'frequency': fields.Integer(
required=True,
description='Frequency of the scheduling in months'),
'label': fields.String(
required=True,
description='Label of the generated operations'),
'value': fields.Float(
required=True,
description='Value of the generated operations'),
'category': fields.String(
required=False,
description='Category of the generated operations'),
'account_id': fields.Integer(
default=None,
readonly=True,
required=True,
description='Account id of the scheduled operation'),
})
# Parser for an account id.
account_id_parser = ns.parser()
account_id_parser.add_argument(
'account_id',
type=int,
required=True,
location='args',
help='Id of the account'
)
# pylint: enable=invalid-name
# pylint: disable=no-self-use
@ns.route('')
@ns.doc(
security='apikey',
responses={
401: 'Unauthorized',
})
class ScheduledOperationListResource(Resource):
"""Resource to handle scheduled operation lists."""
@ns.expect(account_id_parser)
@ns.response(200, 'OK', [scheduled_operation_model])
@ns.marshal_list_with(scheduled_operation_model)
@jwt_required
def get(self):
"""Get all scheduled operation for an account."""
data = account_id_parser.parse_args()
return ScheduledOperation.query().filter_by(**data).all(), 200
@ns.expect(scheduled_operation_model)
@ns.response(200, 'OK', scheduled_operation_model)
@ns.response(404, 'Account not found')
@ns.response(406, 'Invalid operation data')
@ns.marshal_with(scheduled_operation_model)
@jwt_required
def post(self):
"""Add a new scheduled operation."""
data = self.api.payload
account_id = data['account_id']
account = Account.query().get(account_id)
if not account:
ns.abort(404, 'Account with id %d not found.' % account_id)
# A new scheduled operation MUST NOT have an id;
if data.get('id') is not None:
ns.abort(406, 'Id must not be provided on creation.')
scheduled_operation = ScheduledOperation(**data)
db.session.add(scheduled_operation) # pylint: disable=no-member
scheduled_operation.reschedule()
db.session.flush() # pylint: disable=no-member
return scheduled_operation, 201
@ns.route('/<int:scheduled_operation_id>')
@ns.doc(
security='apikey',
params={
'scheduled_operation_id': 'Id of the scheduled operation to manage'
},
responses={
401: 'Unauthorized',
404: 'Scheduled operation not found'
})
class ScheduledOperationResource(Resource):
"""Resource to handle scheduled operations."""
@ns.response(200, 'OK', scheduled_operation_model)
@ns.marshal_with(scheduled_operation_model)
@jwt_required
def get(self, scheduled_operation_id):
"""Get scheduled operation."""
so_id = scheduled_operation_id
scheduled_operation = ScheduledOperation.query().get(so_id)
if not scheduled_operation:
ns.abort(404, 'Scheduled operation with id %d not found.' % so_id)
return scheduled_operation, 200
@ns.response(200, 'OK', scheduled_operation_model)
@ns.response(406, 'Invalid scheduled operation data')
@ns.expect(scheduled_operation_model)
@ns.marshal_with(scheduled_operation_model)
@jwt_required
def post(self, scheduled_operation_id):
"""Update a scheduled operation."""
data = self.api.payload
so_id = scheduled_operation_id
# Check ID consistency.
if data.get('id', so_id) != so_id:
ns.abort(406, 'Id must not be provided or changed on update.')
scheduled_operation = ScheduledOperation.query().get(so_id)
if not scheduled_operation:
ns.abort(404, 'Scheduled operation with id %d not found.' % so_id)
# FIXME check account_id consistency.
# SQLAlchemy objects ignore __dict__.update() with merge.
for key, value in data.items():
setattr(scheduled_operation, key, value)
db.session.merge(scheduled_operation) # pylint: disable=no-member
scheduled_operation.reschedule()
db.session.flush() # pylint: disable=no-member
return scheduled_operation, 200
@ns.response(200, 'OK', scheduled_operation_model)
@ns.response(409, 'Cannot be deleted')
@ns.marshal_with(scheduled_operation_model)
@jwt_required
def delete(self, scheduled_operation_id):
"""Delete a scheduled operation."""
so_id = scheduled_operation_id
scheduled_operation = ScheduledOperation.query().get(so_id)
if not scheduled_operation:
ns.abort(404, 'Scheduled operation with id %d not found.' % so_id)
operations = scheduled_operation.operations.filter(
Operation.confirmed == true()
).count()
if operations:
ns.abort(409, 'There are still confirmed operations '
'associated to this scheduled operation.')
# Delete unconfirmed operations
scheduled_operation.operations.delete()
db.session.delete(scheduled_operation) # pylint: disable=no-member
return None, 204