Merge scheduling and entries in operation.
This commit is contained in:
parent
d9c1052f5e
commit
c8b4c54742
@ -1,69 +0,0 @@
|
|||||||
"""
|
|
||||||
This file is part of Accountant.
|
|
||||||
|
|
||||||
Accountant is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Affero General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Accountant is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
|
||||||
along with Accountant. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from sqlalchemy import distinct, func, case, cast, extract
|
|
||||||
|
|
||||||
from accountant import db
|
|
||||||
|
|
||||||
|
|
||||||
class Entry(db.Model):
|
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
|
||||||
pointed = db.Column(db.Boolean, nullable=False, default=False)
|
|
||||||
operation_date = db.Column(db.Date, nullable=False)
|
|
||||||
label = db.Column(db.String(500), nullable=False)
|
|
||||||
value = db.Column(db.Numeric(15, 2), nullable=False)
|
|
||||||
account_id = db.Column(db.Integer, db.ForeignKey('account.id'))
|
|
||||||
scheduled_operation_id = db.Column(db.Integer,
|
|
||||||
db.ForeignKey('scheduled_operation.id'))
|
|
||||||
|
|
||||||
account = db.relationship("Account", backref=db.backref('entry',
|
|
||||||
lazy="dynamic"))
|
|
||||||
scheduled_operation = db.relationship("ScheduledOperation",
|
|
||||||
backref=db.backref('entry',
|
|
||||||
lazy="dynamic"))
|
|
||||||
|
|
||||||
category = db.Column(db.String(100), nullable=True)
|
|
||||||
|
|
||||||
def __init__(self, pointed, label, value, account_id, operation_date=None,
|
|
||||||
category=None, scheduled_operation_id=None):
|
|
||||||
self.pointed = pointed
|
|
||||||
self.operation_date = operation_date
|
|
||||||
self.label = label
|
|
||||||
self.value = value
|
|
||||||
self.account_id = account_id
|
|
||||||
self.category = category
|
|
||||||
self.scheduled_operation_id = scheduled_operation_id
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_months_for_account(cls, session, account):
|
|
||||||
if isinstance(account, int) or isinstance(account, str):
|
|
||||||
account_id = account
|
|
||||||
else:
|
|
||||||
account_id = account.id
|
|
||||||
|
|
||||||
query = session.query(
|
|
||||||
distinct(func.lpad(cast(extract("year", cls.operation_date),
|
|
||||||
db.String), 4, '0')).label("year"),
|
|
||||||
func.lpad(cast(extract("month", cls.operation_date),
|
|
||||||
db.String), 2, '0').label("month")
|
|
||||||
).filter(cls.account_id == account_id).order_by("year", "month")
|
|
||||||
|
|
||||||
return query
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get(cls, session, id):
|
|
||||||
return session.query(cls).filter(cls.id == id).one()
|
|
@ -16,36 +16,68 @@
|
|||||||
"""
|
"""
|
||||||
import arrow
|
import arrow
|
||||||
|
|
||||||
from sqlalchemy import func, case, desc, false
|
from sqlalchemy import func, case, desc, true, false
|
||||||
|
|
||||||
from accountant import db
|
from accountant import db
|
||||||
|
|
||||||
|
|
||||||
class Operation(db.Model):
|
class Operation(db.Model):
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
pointed = db.Column(db.Boolean, nullable=False, default=False)
|
|
||||||
operation_date = db.Column(db.Date, nullable=False)
|
operation_date = db.Column(db.Date, nullable=False)
|
||||||
label = db.Column(db.String(500), nullable=False)
|
label = db.Column(db.String(500), nullable=False)
|
||||||
value = db.Column(db.Numeric(15, 2), nullable=False)
|
value = db.Column(db.Numeric(15, 2), nullable=False)
|
||||||
account_id = db.Column(db.Integer, db.ForeignKey('account.id'))
|
|
||||||
scheduled_operation_id = db.Column(db.Integer,
|
|
||||||
db.ForeignKey('scheduled_operation.id'))
|
|
||||||
|
|
||||||
account = db.relationship('Account', backref=db.backref('operation',
|
scheduled_operation_id = db.Column(
|
||||||
lazy="dynamic"))
|
db.Integer,
|
||||||
|
db.ForeignKey('scheduled_operation.id')
|
||||||
|
)
|
||||||
|
|
||||||
|
scheduled_operation = db.relationship(
|
||||||
|
"ScheduledOperation", backref=db.backref('entry', lazy="dynamic")
|
||||||
|
)
|
||||||
|
|
||||||
|
account_id = db.Column(db.Integer, db.ForeignKey('account.id'))
|
||||||
|
|
||||||
|
account = db.relationship(
|
||||||
|
'Account', backref=db.backref('operation', lazy="dynamic")
|
||||||
|
)
|
||||||
|
|
||||||
category = db.Column(db.String(100), nullable=True)
|
category = db.Column(db.String(100), nullable=True)
|
||||||
|
|
||||||
canceled = db.Column(db.Boolean, nullable=False)
|
pointed = db.Column(
|
||||||
|
db.Boolean,
|
||||||
|
nullable=False,
|
||||||
|
default=False,
|
||||||
|
server_default=false()
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self, pointed, label, value, account_id, operation_date=None,
|
confirmed = db.Column(
|
||||||
category=None):
|
db.Boolean,
|
||||||
self.pointed = pointed
|
nullable=False,
|
||||||
|
default=True,
|
||||||
|
server_default=true()
|
||||||
|
)
|
||||||
|
|
||||||
|
canceled = db.Column(
|
||||||
|
db.Boolean,
|
||||||
|
nullable=False,
|
||||||
|
default=False,
|
||||||
|
server_default=false()
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, label, value, account_id, operation_date=None,
|
||||||
|
category=None, pointed=False, confirmed=True, canceled=False,
|
||||||
|
scheduled_operation_id=None):
|
||||||
self.operation_date = operation_date
|
self.operation_date = operation_date
|
||||||
self.label = label
|
self.label = label
|
||||||
self.value = value
|
self.value = value
|
||||||
self.account_id = account_id
|
|
||||||
self.category = category
|
self.category = category
|
||||||
|
self.account_id = account_id
|
||||||
|
self.scheduled_operation_id = scheduled_operation_id
|
||||||
|
self.pointed = pointed
|
||||||
|
self.confirmed = confirmed
|
||||||
|
self.canceled = canceled
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_for_account_and_range(cls, session, account, begin, end):
|
def get_for_account_and_range(cls, session, account, begin, end):
|
||||||
|
@ -14,28 +14,42 @@
|
|||||||
You should have received a copy of the GNU Affero General Public License
|
You should have received a copy of the GNU Affero General Public License
|
||||||
along with Accountant. If not, see <http://www.gnu.org/licenses/>.
|
along with Accountant. If not, see <http://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
from sqlalchemy import desc
|
from calendar import monthrange
|
||||||
|
|
||||||
|
from sqlalchemy import desc, false
|
||||||
|
|
||||||
|
import arrow
|
||||||
|
|
||||||
from accountant import db
|
from accountant import db
|
||||||
|
|
||||||
from .accounts import Account
|
from .accounts import Account
|
||||||
|
from .operations import Operation
|
||||||
|
|
||||||
|
|
||||||
class ScheduledOperation(db.Model):
|
class ScheduledOperation(db.Model):
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
|
||||||
start_date = db.Column(db.Date, nullable=False)
|
start_date = db.Column(db.Date, nullable=False)
|
||||||
stop_date = db.Column(db.Date, nullable=False)
|
stop_date = db.Column(db.Date, nullable=False)
|
||||||
|
|
||||||
day = db.Column(db.Integer, nullable=False)
|
day = db.Column(db.Integer, nullable=False)
|
||||||
frequency = db.Column(db.Integer, nullable=False)
|
frequency = db.Column(db.Integer, nullable=False)
|
||||||
|
|
||||||
label = db.Column(db.String(500), nullable=False)
|
label = db.Column(db.String(500), nullable=False)
|
||||||
value = db.Column(db.Numeric(15, 2), nullable=False)
|
value = db.Column(db.Numeric(15, 2), nullable=False)
|
||||||
account_id = db.Column(db.Integer, db.ForeignKey('account.id'))
|
|
||||||
|
|
||||||
account = db.relationship(Account, backref=db.backref('scheduled_operation',
|
|
||||||
lazy="dynamic"))
|
|
||||||
|
|
||||||
category = db.Column(db.String(100), nullable=True)
|
category = db.Column(db.String(100), nullable=True)
|
||||||
|
|
||||||
|
account_id = db.Column(
|
||||||
|
db.Integer,
|
||||||
|
db.ForeignKey('account.id')
|
||||||
|
)
|
||||||
|
|
||||||
|
account = db.relationship(
|
||||||
|
Account,
|
||||||
|
backref=db.backref('scheduled_operation', lazy="dynamic")
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self, start_date, stop_date, day, frequency, label, value,
|
def __init__(self, start_date, stop_date, day, frequency, label, value,
|
||||||
account_id, category=None):
|
account_id, category=None):
|
||||||
self.start_date = start_date
|
self.start_date = start_date
|
||||||
@ -56,3 +70,50 @@ class ScheduledOperation(db.Model):
|
|||||||
cls.value,
|
cls.value,
|
||||||
cls.label,
|
cls.label,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def reschedule(self, session):
|
||||||
|
# 1) delete unconfirmed operations for this account.
|
||||||
|
session.query(
|
||||||
|
Operation
|
||||||
|
).filter(
|
||||||
|
Operation.scheduled_operation_id == self.id,
|
||||||
|
Operation.confirmed == false()
|
||||||
|
).delete()
|
||||||
|
|
||||||
|
# 2) schedule remaining operations.
|
||||||
|
start_date = arrow.get(self.start_date)
|
||||||
|
stop_date = arrow.get(self.stop_date)
|
||||||
|
|
||||||
|
for r in arrow.Arrow.range(
|
||||||
|
"month", start_date, stop_date
|
||||||
|
)[::self.frequency]:
|
||||||
|
current_monthrange = monthrange(r.year, r.month)
|
||||||
|
|
||||||
|
day = max(self.day, current_monthrange[0])
|
||||||
|
day = min(self.day, current_monthrange[1])
|
||||||
|
|
||||||
|
r.replace(day=day)
|
||||||
|
|
||||||
|
# Search if a close operation exists.
|
||||||
|
if not session.query(
|
||||||
|
Operation
|
||||||
|
).filter(
|
||||||
|
Operation.account_id == self.account_id,
|
||||||
|
Operation.scheduled_operation_id == self.id,
|
||||||
|
Operation.operation_date >= r.clone().replace(weeks=-2).date(),
|
||||||
|
Operation.operation_date <= r.clone().replace(weeks=+2).date()
|
||||||
|
).count():
|
||||||
|
# Create not confirmed operation if not found.
|
||||||
|
operation = Operation(
|
||||||
|
operation_date=r.date(),
|
||||||
|
label=self.label,
|
||||||
|
value=self.value,
|
||||||
|
category=self.category,
|
||||||
|
account_id=self.account_id,
|
||||||
|
scheduled_operation_id=self.id,
|
||||||
|
pointed=False,
|
||||||
|
confirmed=False,
|
||||||
|
canceled=False
|
||||||
|
)
|
||||||
|
|
||||||
|
session.add(operation)
|
||||||
|
@ -24,7 +24,6 @@ from accountant import session_aware
|
|||||||
|
|
||||||
from .. import api_api
|
from .. import api_api
|
||||||
|
|
||||||
from ..models.entries import Entry
|
|
||||||
from ..models.operations import Operation
|
from ..models.operations import Operation
|
||||||
|
|
||||||
from ..fields import Object
|
from ..fields import Object
|
||||||
@ -40,6 +39,7 @@ resource_fields = {
|
|||||||
'account_id': fields.Integer,
|
'account_id': fields.Integer,
|
||||||
'scheduled_operation_id': fields.Integer(default=None),
|
'scheduled_operation_id': fields.Integer(default=None),
|
||||||
'sold': fields.Float,
|
'sold': fields.Float,
|
||||||
|
'confirmed': fields.Boolean,
|
||||||
'canceled': fields.Boolean,
|
'canceled': fields.Boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,6 +53,8 @@ parser.add_argument('pointed', type=bool)
|
|||||||
parser.add_argument('category', type=str)
|
parser.add_argument('category', type=str)
|
||||||
parser.add_argument('account_id', type=int)
|
parser.add_argument('account_id', type=int)
|
||||||
parser.add_argument('scheduled_operation_id', type=int)
|
parser.add_argument('scheduled_operation_id', type=int)
|
||||||
|
parser.add_argument('confirmed', type=bool)
|
||||||
|
parser.add_argument('canceled', type=bool)
|
||||||
|
|
||||||
|
|
||||||
range_parser = reqparse.RequestParser()
|
range_parser = reqparse.RequestParser()
|
||||||
@ -61,7 +63,7 @@ range_parser.add_argument('begin', type=lambda a: dateutil.parser.parse(a))
|
|||||||
range_parser.add_argument('end', type=lambda a: dateutil.parser.parse(a))
|
range_parser.add_argument('end', type=lambda a: dateutil.parser.parse(a))
|
||||||
|
|
||||||
|
|
||||||
class EntryListResource(Resource):
|
class OperationListResource(Resource):
|
||||||
@session_aware
|
@session_aware
|
||||||
@marshal_with_field(fields.List(Object(resource_fields)))
|
@marshal_with_field(fields.List(Object(resource_fields)))
|
||||||
def get(self, session):
|
def get(self, session):
|
||||||
@ -77,57 +79,57 @@ class EntryListResource(Resource):
|
|||||||
def post(self, session):
|
def post(self, session):
|
||||||
kwargs = parser.parse_args()
|
kwargs = parser.parse_args()
|
||||||
|
|
||||||
entry = Entry(**kwargs)
|
operation = Operation(**kwargs)
|
||||||
|
|
||||||
session.add(entry)
|
session.add(operation)
|
||||||
|
|
||||||
return entry
|
return operation
|
||||||
|
|
||||||
|
|
||||||
class EntryResource(Resource):
|
class OperationResource(Resource):
|
||||||
@session_aware
|
@session_aware
|
||||||
@marshal_with_field(Object(resource_fields))
|
@marshal_with_field(Object(resource_fields))
|
||||||
def get(self, entry_id, session):
|
def get(self, operation_id, session):
|
||||||
"""
|
"""
|
||||||
Get entry.
|
Get operation.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return Entry.get(session, entry_id)
|
return Operation.get(session, operation_id)
|
||||||
except NoResultFound:
|
except NoResultFound:
|
||||||
return None, 404
|
return None, 404
|
||||||
|
|
||||||
@session_aware
|
@session_aware
|
||||||
@marshal_with_field(Object(resource_fields))
|
@marshal_with_field(Object(resource_fields))
|
||||||
def post(self, entry_id, session):
|
def post(self, operation_id, session):
|
||||||
kwargs = parser.parse_args()
|
kwargs = parser.parse_args()
|
||||||
|
|
||||||
assert (id not in kwargs or kwargs.id is None
|
assert (id not in kwargs or kwargs.id is None
|
||||||
or kwargs.id == entry_id)
|
or kwargs.id == operation_id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
entry = Entry.get(session, entry_id)
|
operation = Operation.get(session, operation_id)
|
||||||
except NoResultFound:
|
except NoResultFound:
|
||||||
return None, 404
|
return None, 404
|
||||||
|
|
||||||
# SQLAlchemy objects ignore __dict__.update() with merge.
|
# SQLAlchemy objects ignore __dict__.update() with merge.
|
||||||
for k, v in kwargs.items():
|
for k, v in kwargs.items():
|
||||||
setattr(entry, k, v)
|
setattr(operation, k, v)
|
||||||
|
|
||||||
session.merge(entry)
|
session.merge(operation)
|
||||||
|
|
||||||
return entry
|
return operation
|
||||||
|
|
||||||
@session_aware
|
@session_aware
|
||||||
@marshal_with_field(Object(resource_fields))
|
@marshal_with_field(Object(resource_fields))
|
||||||
def delete(self, entry_id, session):
|
def delete(self, operation_id, session):
|
||||||
try:
|
try:
|
||||||
entry = Entry.get(session, entry_id)
|
operation = Operation.get(session, operation_id)
|
||||||
except NoResultFound:
|
except NoResultFound:
|
||||||
return None, 404
|
return None, 404
|
||||||
|
|
||||||
session.delete(entry)
|
session.delete(operation)
|
||||||
|
|
||||||
return entry
|
return operation
|
||||||
|
|
||||||
|
|
||||||
category_resource_fields = {
|
category_resource_fields = {
|
||||||
@ -164,7 +166,7 @@ class SoldsResource(Resource):
|
|||||||
return Operation.get_ohlc_per_day_for_range(session, **kwargs).all()
|
return Operation.get_ohlc_per_day_for_range(session, **kwargs).all()
|
||||||
|
|
||||||
|
|
||||||
api_api.add_resource(EntryListResource, "/entries")
|
api_api.add_resource(OperationListResource, "/entries")
|
||||||
api_api.add_resource(EntryResource, "/entries/<int:entry_id>")
|
api_api.add_resource(OperationResource, "/entries/<int:operation_id>")
|
||||||
api_api.add_resource(CategoriesResource, "/categories")
|
api_api.add_resource(CategoriesResource, "/categories")
|
||||||
api_api.add_resource(SoldsResource, "/solds")
|
api_api.add_resource(SoldsResource, "/solds")
|
||||||
|
@ -80,11 +80,13 @@ class ScheduledOperationListResource(Resource):
|
|||||||
"""
|
"""
|
||||||
kwargs = parser.parse_args()
|
kwargs = parser.parse_args()
|
||||||
|
|
||||||
scheduledOperation = ScheduledOperation(**kwargs)
|
scheduled_operation = ScheduledOperation(**kwargs)
|
||||||
|
|
||||||
session.add(scheduledOperation)
|
session.add(scheduled_operation)
|
||||||
|
|
||||||
return scheduledOperation, 201
|
scheduled_operation.reschedule(session)
|
||||||
|
|
||||||
|
return scheduled_operation, 201
|
||||||
|
|
||||||
|
|
||||||
class ScheduledOperationResource(Resource):
|
class ScheduledOperationResource(Resource):
|
||||||
@ -109,6 +111,9 @@ class ScheduledOperationResource(Resource):
|
|||||||
"""
|
"""
|
||||||
Delete a scheduled operation.
|
Delete a scheduled operation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
raise NotImplementedError("Must be fixed.")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
scheduled_operation = ScheduledOperation.query(
|
scheduled_operation = ScheduledOperation.query(
|
||||||
session
|
session
|
||||||
@ -148,6 +153,8 @@ class ScheduledOperationResource(Resource):
|
|||||||
|
|
||||||
session.merge(scheduled_operation)
|
session.merge(scheduled_operation)
|
||||||
|
|
||||||
|
scheduled_operation.reschedule(session)
|
||||||
|
|
||||||
return scheduled_operation
|
return scheduled_operation
|
||||||
|
|
||||||
|
|
||||||
|
@ -396,7 +396,7 @@ accountantApp
|
|||||||
|
|
||||||
// Returns true if the entry is a scheduled one.
|
// Returns true if the entry is a scheduled one.
|
||||||
$scope.isSaved = function(entry) {
|
$scope.isSaved = function(entry) {
|
||||||
return entry.id !== null;
|
return entry.confirmed;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Cancel current editing entry or clears field if a new one.
|
// Cancel current editing entry or clears field if a new one.
|
||||||
@ -426,11 +426,19 @@ accountantApp
|
|||||||
|
|
||||||
// Points an entry.
|
// Points an entry.
|
||||||
$scope.pointEntry = function(entry) {
|
$scope.pointEntry = function(entry) {
|
||||||
|
entry.confirmed = true;
|
||||||
entry.pointed = !entry.pointed;
|
entry.pointed = !entry.pointed;
|
||||||
|
|
||||||
$scope.saveEntry(entry);
|
$scope.saveEntry(entry);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Confirm an entry.
|
||||||
|
$scope.confirmEntry = function(entry) {
|
||||||
|
entry.confirmed = true;
|
||||||
|
|
||||||
|
$scope.saveEntry(entry);
|
||||||
|
};
|
||||||
|
|
||||||
// Create an new entry.
|
// Create an new entry.
|
||||||
$scope.createEntry = function(entry) {
|
$scope.createEntry = function(entry) {
|
||||||
entry.account_id = $scope.account.id;
|
entry.account_id = $scope.account.id;
|
||||||
|
@ -171,7 +171,7 @@
|
|||||||
|
|
||||||
<!-- Button group for an unsaved (scheduled) entry. -->
|
<!-- Button group for an unsaved (scheduled) entry. -->
|
||||||
<div class="btn-group" ng-if="!isSaved(entry)">
|
<div class="btn-group" ng-if="!isSaved(entry)">
|
||||||
<button class="btn btn-xs btn-success" ng-click="saveEntry(entry)" title="Save">
|
<button class="btn btn-xs btn-success" ng-click="confirmEntry(entry)" title="Save">
|
||||||
<span class="fa fa-plus"></span>
|
<span class="fa fa-plus"></span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
144
migrations/versions/144929e0f5f_improve_operation_scheduling.py
Normal file
144
migrations/versions/144929e0f5f_improve_operation_scheduling.py
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
"""Improve operation scheduling.
|
||||||
|
|
||||||
|
Revision ID: 144929e0f5f
|
||||||
|
Revises: None
|
||||||
|
Create Date: 2015-07-17 15:04:01.002581
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '144929e0f5f'
|
||||||
|
down_revision = None
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from accountant import session_scope
|
||||||
|
from accountant.api.models.scheduled_operations import ScheduledOperation
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.get_bind().execute("DROP VIEW operation")
|
||||||
|
op.rename_table('entry', 'operation')
|
||||||
|
|
||||||
|
# Add column "canceled" in table "entry"
|
||||||
|
op.add_column(
|
||||||
|
'operation',
|
||||||
|
sa.Column(
|
||||||
|
'canceled',
|
||||||
|
sa.Boolean(),
|
||||||
|
nullable=False,
|
||||||
|
default=False,
|
||||||
|
server_default=sa.false()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add column "confirmed" in table "entry"
|
||||||
|
op.add_column(
|
||||||
|
'operation',
|
||||||
|
sa.Column(
|
||||||
|
'confirmed',
|
||||||
|
sa.Boolean(),
|
||||||
|
nullable=False,
|
||||||
|
default=True,
|
||||||
|
server_default=sa.true()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Drop unused table canceled_operation.
|
||||||
|
op.drop_table('canceled_operation')
|
||||||
|
|
||||||
|
op.get_bind().execute(
|
||||||
|
"alter sequence entry_id_seq rename to operation_id_seq"
|
||||||
|
)
|
||||||
|
|
||||||
|
# TODO Alexis Lahouze 2015-07-17 Insert scheduling.
|
||||||
|
connection = op.get_bind()
|
||||||
|
Session = sa.orm.sessionmaker()
|
||||||
|
session = Session(bind=connection)
|
||||||
|
|
||||||
|
# Get all scheduled operations
|
||||||
|
scheduled_operations = ScheduledOperation.query(session).all()
|
||||||
|
|
||||||
|
for scheduled_operation in scheduled_operations:
|
||||||
|
scheduled_operation.reschedule(session)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
op.get_bind().execute(
|
||||||
|
"alter sequence operation_id_seq rename to entry_id_seq"
|
||||||
|
)
|
||||||
|
|
||||||
|
op.create_table(
|
||||||
|
"canceled_operation",
|
||||||
|
sa.Column("id", sa.Integer, primary_key=True),
|
||||||
|
sa.Column(
|
||||||
|
"scheduled_operation_id", sa.Integer(),
|
||||||
|
sa.ForeignKey("scheduled_operation.id")),
|
||||||
|
sa.Column("operation_date", sa.Date, nullable=False)
|
||||||
|
)
|
||||||
|
|
||||||
|
op.drop_column('operation', 'canceled')
|
||||||
|
op.drop_column('operation', 'confirmed')
|
||||||
|
|
||||||
|
op.rename_table('operation', 'entry')
|
||||||
|
|
||||||
|
op.get_bind().execute(
|
||||||
|
"""
|
||||||
|
CREATE VIEW operation AS
|
||||||
|
SELECT entry.id,
|
||||||
|
entry.operation_date,
|
||||||
|
entry.label,
|
||||||
|
entry.value,
|
||||||
|
entry.account_id,
|
||||||
|
entry.category,
|
||||||
|
entry.pointed,
|
||||||
|
entry.scheduled_operation_id,
|
||||||
|
false AS canceled
|
||||||
|
FROM entry
|
||||||
|
UNION
|
||||||
|
SELECT NULL::bigint AS id,
|
||||||
|
scheduled_operation.operation_date,
|
||||||
|
scheduled_operation.label,
|
||||||
|
scheduled_operation.value,
|
||||||
|
scheduled_operation.account_id,
|
||||||
|
scheduled_operation.category,
|
||||||
|
false AS pointed,
|
||||||
|
scheduled_operation.id AS scheduled_operation_id,
|
||||||
|
false AS canceled
|
||||||
|
FROM (
|
||||||
|
SELECT scheduled_operation_1.id,
|
||||||
|
scheduled_operation_1.start_date,
|
||||||
|
scheduled_operation_1.stop_date,
|
||||||
|
scheduled_operation_1.day,
|
||||||
|
scheduled_operation_1.frequency,
|
||||||
|
scheduled_operation_1.label,
|
||||||
|
scheduled_operation_1.value,
|
||||||
|
scheduled_operation_1.account_id,
|
||||||
|
scheduled_operation_1.category,
|
||||||
|
generate_series(scheduled_operation_1.start_date::timestamp without time zone, scheduled_operation_1.stop_date::timestamp without time zone, scheduled_operation_1.frequency::double precision * '1 mon'::interval) AS operation_date
|
||||||
|
FROM scheduled_operation scheduled_operation_1) scheduled_operation
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT entry.scheduled_operation_id,
|
||||||
|
date_trunc('MONTH'::text, entry.operation_date::timestamp with time zone) AS operation_date
|
||||||
|
FROM entry
|
||||||
|
UNION
|
||||||
|
SELECT canceled_operation.scheduled_operation_id,
|
||||||
|
date_trunc('MONTH'::text, canceled_operation.operation_date::timestamp with time zone) AS operation_date
|
||||||
|
FROM canceled_operation
|
||||||
|
) saved_operation ON saved_operation.scheduled_operation_id = scheduled_operation.id AND saved_operation.operation_date = date_trunc('MONTH'::text, scheduled_operation.operation_date)
|
||||||
|
WHERE saved_operation.scheduled_operation_id IS NULL
|
||||||
|
UNION
|
||||||
|
SELECT NULL::bigint AS id,
|
||||||
|
canceled_operation.operation_date,
|
||||||
|
scheduled_operation.label,
|
||||||
|
scheduled_operation.value,
|
||||||
|
scheduled_operation.account_id,
|
||||||
|
scheduled_operation.category,
|
||||||
|
false AS pointed,
|
||||||
|
scheduled_operation.id AS scheduled_operation_id,
|
||||||
|
true AS canceled
|
||||||
|
FROM scheduled_operation
|
||||||
|
JOIN canceled_operation ON canceled_operation.scheduled_operation_id = scheduled_operation.id;
|
||||||
|
"""
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user