Hi I am experimenting with MEANjs for development.
I have made an app that consist of CRUD for "Accounts", "Incomes" and "Expenses". Each of which are created using yo meanjs:crud-module <module-name>
. Now I wont to expand my project with a type: "Transfers", that consist of both an income and an expense, i.e. creating a transfer creates an expense on the sending account and an income on a receiving account.
I want to create this as easily and logical as I possibly can.
I imagine putting the most of the logic on the server side as possible will make sense. So that the frontend identifies the transfer as a transfer object that is created in the backend controller by combining data from the income and expense models.
To identify that an income actually belongs to a transfer I add the following attributes to the model, where i tell if it is a transfer using the attribute: isTransfer, and which expense it belongs to using the "expense" attribute.
'use strict';
/**
* Module dependencies.
*/
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
/**
* Income Schema
*/
var IncomeSchema = new Schema({
name: {
type: String,
default: '',
required: 'Please fill Income name',
trim: true
},
amount: {
type: String,
default: '',
required: 'Please fill Income amount',
trim: true
},
date: {
type: Date,
default: '',
required: 'Please fill Income date',
trim: true
},
monthly: {
type: Boolean,
default: '',
required: 'Please fill whether income is recurring monthly',
trim: true
},
yearly: {
type: Boolean,
default: '',
required: 'Please fill whether income is recurring yearly',
trim: true
},
account: {
type: Schema.ObjectId,
ref: 'Account',
required: 'Please select account'
},
isTransfer: {
type: Boolean,
default: false,
trim: true
},
expense:{
type: Schema.ObjectId,
ref:'Expense'
},
created: {
type: Date,
default: Date.now
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
mongoose.model('Income', IncomeSchema);
Then I imagine creating the transfer in the entire stack would be as easy as running:
yo meanjs:crud-module transfers
my next step would be to delete the model for transfers in backend and rewrite the backend controller so that it creates an income and an expense every time i try to create a transfer.
The controller then also returns an actual "Transfer object" all the way up to the frontend from the backend controller.
Initially I tried adding the transfers object as only existing in the frontend and sending requests to create incomes and expenses all the way from the frontend service but it clogged up the code a lot and I had to do many calls to the backend to create one transfer. I.e.
- create income -> save resource call for income
- get income id from response
- create expense with the income id -> save resource call for expense
- take expense id from response
- update the income with the expense id. -> save resource call for income again
I think it would make more sense to create the transfer object in the backend controller. Is this a reasonable way of managing the transfer objects?
Does it make more sense to create transfers in another way? Or on another level of the stack?
I guess I could create a model for transfer that contains just references to incomes and expenses, but I still would need to create the income/expense objects right:
Sooo, I ran the yo meanjs:crud-module <module-name>
command and generated a transfers module, and then removed the entire transfers model, and commented it out from the server controller. I kept the frontend representation though, list and create, edit etc. the server controller needs to be reworked so that it relates to income and expense models instead. Example below:
The server Transfers controller's create method thus becomes an abomination of income and expense like soo:
/**
* Create a Income and Expense
*/
exports.create = function(req, res) {
var income = new Income(req.body);
var expense = new Expense(req.body);
expense.user = income.user = req.user;
console.log('Request: ' + JSON.stringify(req.body));
//This should create an income and expense that is part of a transfer
expense.isTransfer = income.isTransfer = true;
//Generate ID's manually to create the relation between income and expense:
income.expense = expense._id = mongoose.Types.ObjectId();
expense.income = income._id = mongoose.Types.ObjectId();
//Set income and expense receiving and sending account, these attributes where simply attached to the body:
income.account = req.body.receivingAccount;
expense.account = req.body.sendingAccount;
//Save income if successfull, also save expense, if both succeeds return income and expense
income.save(function(err) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
expense.save(function(err) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.jsonp({income: income, expense: expense});
}
});
}
});
};
And the client controller for create Transfer, not as ugly, note that the Transfer object created does not correspond to the transfer object defined in the mongoose model:
// Create new Transfer when received in the server controller this yields both an income and an expense.
$scope.create = function() {
// Create new Transfer object, this defines what your request.body will contain.
var transfer = new Transfers ({
name: this.name,
amount: this.amount,
date: this.date,
monthly: this.recurring==='monthly',
yearly: this.recurring==='yearly',
receivingAccount: this.receivingAccount._id,
sendingAccount: this.sendingAccount._id
});
// Redirect after save
transfer.$save(function(response) {
$location.path('transfers/' + response._id);
// Clear form fields
$scope.name = '';
}, function(errorResponse) {
$scope.error = errorResponse.data.message;
});
};
Thanks