Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions api/categories/controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,69 @@ def post(self):
return make_response(jsonify({
'message': f'Error processing CSV: {str(e)}'
}), 500)


@g.api.route('/categories/<string:id>')
class CategoriesDetail(Resource):
def get(self, id):
"""Get a single category by ID"""
category = CategoriesModel.query.get(id)
if not category:
return make_response(jsonify({'message': 'Category not found'}), 404)

return make_response(jsonify({'category': category.to_dict()}), 200)

@g.api.expect(categories_model)
def put(self, id):
"""Update a category"""
category = CategoriesModel.query.get(id)
if not category:
return make_response(jsonify({'message': 'Category not found'}), 404)

data = request.json

# Validate foreign keys if provided
if 'categories_group_id' in data:
group = CategoriesGroupModel.query.get(data['categories_group_id'])
if not group:
return make_response(jsonify({
'message': 'Invalid categories_group_id'
}), 400)

if 'categories_type_id' in data:
cat_type = CategoriesTypeModel.query.get(data['categories_type_id'])
if not cat_type:
return make_response(jsonify({
'message': 'Invalid categories_type_id'
}), 400)

# Update fields if provided
if 'name' in data:
category.name = data['name']
if 'categories_group_id' in data:
category.categories_group_id = data['categories_group_id']
if 'categories_type_id' in data:
category.categories_type_id = data['categories_type_id']

category.save()

return make_response(jsonify({'message': 'Category updated successfully', 'category': category.to_dict()}), 200)

def delete(self, id):
"""Delete a category"""
from api.transaction.models import TransactionModel

category = CategoriesModel.query.get(id)
if not category:
return make_response(jsonify({'message': 'Category not found'}), 404)

# Check if there are any transactions linked to this category
linked_transactions = TransactionModel.query.filter_by(categories_id=id).count()
if linked_transactions > 0:
return make_response(jsonify({
'message': f'Cannot delete category. There are {linked_transactions} transaction(s) linked to it. Please delete or reassign the transactions first.'
}), 400)

category.delete()

return make_response(jsonify({'message': 'Category deleted successfully'}), 200)
50 changes: 50 additions & 0 deletions api/institution/controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,53 @@ def get(self):
institutions = InstitutionModel.query.all()
_isntitutions = [institution.to_dict() for institution in institutions]
return make_response(jsonify({'institutions': _isntitutions}), 200)

@g.api.route('/institution/<string:id>')
class InstitutionDetail(Resource):
def get(self, id):
"""Get a single institution by ID"""
institution = InstitutionModel.query.get(id)
if not institution:
return make_response(jsonify({'message': 'Institution not found'}), 404)

return make_response(jsonify({'institution': institution.to_dict()}), 200)

@g.api.expect(institution_model)
def put(self, id):
"""Update an institution"""
institution = InstitutionModel.query.get(id)
if not institution:
return make_response(jsonify({'message': 'Institution not found'}), 404)

data = request.json

# Update fields if provided
if 'name' in data:
institution.name = data['name']
if 'location' in data:
institution.location = data['location']
if 'description' in data:
institution.description = data['description']

institution.save()

return make_response(jsonify({'message': 'Institution updated successfully', 'institution': institution.to_dict()}), 200)

def delete(self, id):
"""Delete an institution"""
from api.institution_account.models import InstitutionAccountModel

institution = InstitutionModel.query.get(id)
if not institution:
return make_response(jsonify({'message': 'Institution not found'}), 404)

# Check if there are any accounts linked to this institution
linked_accounts = InstitutionAccountModel.query.filter_by(institution_id=id).count()
if linked_accounts > 0:
return make_response(jsonify({
'message': f'Cannot delete institution. There are {linked_accounts} account(s) linked to it. Please delete or reassign the accounts first.'
}), 400)

institution.delete()

return make_response(jsonify({'message': 'Institution deleted successfully'}), 200)
83 changes: 82 additions & 1 deletion api/institution_account/controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,85 @@ def get(self):
new_account_balance = starting_balance + total
# we need to offset the transactions
print(f'Updating account {account.name} balance from {account.balance} to {new_account_balance}')

account.balance = new_account_balance
account.save()
__accounts.append(account.to_dict())
return make_response(jsonify({'accounts': __accounts}), 200)

@g.api.route('/institution/account/<string:id>')
class InstitutionAccountDetail(Resource):
def get(self, id):
"""Get a single account by ID"""
account = InstitutionAccountModel.query.get(id)
if not account:
return make_response(jsonify({'message': 'Account not found'}), 404)

return make_response(jsonify({'account': account.to_dict()}), 200)

@g.api.expect(institution_account_model)
def put(self, id):
"""Update an account"""
account = InstitutionAccountModel.query.get(id)
if not account:
return make_response(jsonify({'message': 'Account not found'}), 404)

data = request.json

# Validate enum fields if provided
valid_statuses = ['active', 'inactive']
valid_types = ['checking', 'savings', 'credit', 'loan', 'investment', 'other']
valid_classes = ['asset', 'liability']

if 'status' in data and data['status'] and data['status'] not in valid_statuses:
return make_response(jsonify({
'message': f'Invalid status. Must be one of: {valid_statuses}'
}), 400)

if 'account_type' in data and data['account_type'] and data['account_type'] not in valid_types:
return make_response(jsonify({
'message': f'Invalid account type. Must be one of: {valid_types}'
}), 400)

if 'account_class' in data and data['account_class'] and data['account_class'] not in valid_classes:
return make_response(jsonify({
'message': f'Invalid account class. Must be one of: {valid_classes}'
}), 400)

# Update fields if provided
if 'name' in data:
account.name = data['name']
if 'number' in data:
account.number = data['number']
if 'status' in data:
account.status = data['status']
if 'balance' in data:
account.balance = data['balance']
if 'starting_balance' in data:
account.starting_balance = data['starting_balance']
if 'account_type' in data:
account.account_type = data['account_type']
if 'account_class' in data:
account.account_class = data['account_class']
if 'institution_id' in data:
account.institution_id = data['institution_id']

account.save()

return make_response(jsonify({'message': 'Account updated successfully', 'account': account.to_dict()}), 200)

def delete(self, id):
"""Delete an account"""
account = InstitutionAccountModel.query.get(id)
if not account:
return make_response(jsonify({'message': 'Account not found'}), 404)

# Check if there are any transactions linked to this account
linked_transactions = TransactionModel.query.filter_by(account_id=id).count()
if linked_transactions > 0:
return make_response(jsonify({
'message': f'Cannot delete account. There are {linked_transactions} transaction(s) linked to it. Please delete the transactions first.'
}), 400)

account.delete()

return make_response(jsonify({'message': 'Account deleted successfully'}), 200)
74 changes: 74 additions & 0 deletions api/transaction/controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,3 +362,77 @@ def create_transaction(self,user_id, categories_id, account_id, amount, transact
db.session.add(transaction)
db.session.commit()
print(f"Transaction {external_id} created successfully.")


@g.api.route('/transaction/<string:id>')
class TransactionDetail(Resource):
def get(self, id):
"""Get a single transaction by ID"""
transaction = TransactionModel.query.get(id)
if not transaction:
return make_response(jsonify({'message': 'Transaction not found'}), 404)

return make_response(jsonify({'transaction': transaction.to_dict()}), 200)

@g.api.expect(transaction_model)
def put(self, id):
"""Update a transaction"""
transaction = TransactionModel.query.get(id)
if not transaction:
return make_response(jsonify({'message': 'Transaction not found'}), 404)

data = request.json

# Validate amount if provided
if 'amount' in data:
try:
amount = float(data['amount'])
except (ValueError, TypeError):
return make_response(jsonify({
'message': 'Amount must be a valid number'
}), 400)

# Validate foreign keys if provided
if 'categories_id' in data:
category = CategoriesModel.query.get(data['categories_id'])
if not category:
return make_response(jsonify({
'message': 'Invalid categories_id'
}), 400)

if 'account_id' in data:
account = InstitutionAccountModel.query.get(data['account_id'])
if not account:
return make_response(jsonify({
'message': 'Invalid account_id'
}), 400)

# Update fields if provided
if 'description' in data:
transaction.description = data['description']
if 'amount' in data:
transaction.amount = float(data['amount'])
if 'categories_id' in data:
transaction.categories_id = data['categories_id']
if 'account_id' in data:
transaction.account_id = data['account_id']
if 'transaction_type' in data:
transaction.transaction_type = data['transaction_type']
if 'external_date' in data:
transaction.external_date = data['external_date']
if 'external_id' in data:
transaction.external_id = data['external_id']

transaction.save()

return make_response(jsonify({'message': 'Transaction updated successfully', 'transaction': transaction.to_dict()}), 200)

def delete(self, id):
"""Delete a transaction"""
transaction = TransactionModel.query.get(id)
if not transaction:
return make_response(jsonify({'message': 'Transaction not found'}), 404)

transaction.delete()

return make_response(jsonify({'message': 'Transaction deleted successfully'}), 200)
84 changes: 84 additions & 0 deletions app/static/js/categories/categories.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,88 @@ function categoriesFormSubmit(event) {
.catch((error) => {
console.error('Error:', error);
});
}

async function editCategory(categoryId) {
// Fetch the category data
try {
const response = await fetch(`/api/categories/${categoryId}`);
const data = await response.json();

if (response.ok) {
// Populate the edit form
document.getElementById('edit_category_id').value = categoryId;
document.getElementById('edit_categoriesName').value = data.category.name;
document.getElementById('edit_categoriesGroupId').value = data.category.categories_group_id || '';
document.getElementById('edit_categoriesTypeId').value = data.category.categories_type_id || '';

// Show the modal
const editModal = new bootstrap.Modal(document.getElementById('EditCategoryModal'));
editModal.show();
} else {
alert('Error loading category: ' + data.message);
}
} catch (error) {
console.error('Error:', error);
alert('Error loading category');
}
}

function updateCategory(event) {
event.preventDefault();

const categoryId = document.getElementById('edit_category_id').value;
const categoryName = document.getElementById('edit_categoriesName').value;
const categoriesGroupId = document.getElementById('edit_categoriesGroupId').value;
const categoriesTypeId = document.getElementById('edit_categoriesTypeId').value;
const user_id = document.getElementById('edit_category_user_id').value;

const data = {
name: categoryName,
categories_group_id: categoriesGroupId,
categories_type_id: categoriesTypeId,
user_id: user_id
};

fetch(`/api/categories/${categoryId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
.then(response => response.json())
.then(data => {
console.log('Success:', data);
// redirect to the categories page
window.location.href = '/categories';
})
.catch((error) => {
console.error('Error:', error);
alert('Error updating category');
});
}

async function deleteCategory(categoryId) {
if (!confirm('Are you sure you want to delete this category?')) {
return;
}

try {
const response = await fetch(`/api/categories/${categoryId}`, {
method: 'DELETE',
});

const data = await response.json();

if (response.ok) {
alert('Category deleted successfully');
window.location.href = '/categories';
} else {
alert('Error deleting category: ' + data.message);
}
} catch (error) {
console.error('Error:', error);
alert('Error deleting category');
}
}
Loading
Loading