├── .gitignore
├── Procfile
├── _config.yml
├── src
├── db
│ └── mongoose.js
├── models
│ ├── accountModel.js
│ ├── flexBudgetModel.js
│ ├── trialModel.js
│ ├── budgetModel.js
│ ├── inventoryModel.js
│ ├── journalModel.js
│ ├── ledgerModel.js
│ └── userModel.js
├── middleware
│ └── auth.js
├── routes
│ ├── trialRoute.js
│ ├── budgetRoute.js
│ ├── finalAccRoute.js
│ ├── userRoute.js
│ ├── flexBudgetRoute.js
│ ├── ledgerRoute.js
│ ├── inventoryRoute.js
│ └── journalRoute.js
└── app.js
├── package.json
├── .github
├── workflows
│ └── nodejs.yml
└── FUNDING.yml
├── license
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: node src/app.js
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-merlot
--------------------------------------------------------------------------------
/src/db/mongoose.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose")
2 |
3 | const localDB = 'mongodb://localhost:27017/accountApp'
4 | mongoose.connect(remoteDB, {
5 | useNewUrlParser: true,
6 | useCreateIndex: true,
7 | useFindAndModify: false
8 | })
9 |
10 | const db = mongoose.connection
11 |
12 | module.exports = db
13 |
14 |
--------------------------------------------------------------------------------
/src/models/accountModel.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose")
2 | const bcrypt = require("bcrypt")
3 |
4 | const accountSchema = new mongoose.Schema({
5 | name: {
6 | type: String,
7 | required: true,
8 | trim: true
9 | },
10 | user: {
11 | type: mongoose.Schema.Types.ObjectId,
12 | ref: "User"
13 | }
14 | }, {
15 | timestamps: true
16 | })
17 |
18 | const Account = mongoose.model("Account", accountSchema)
19 |
20 | module.exports = Account
--------------------------------------------------------------------------------
/src/models/flexBudgetModel.js:
--------------------------------------------------------------------------------
1 | var mongoose = require('mongoose');
2 | var Schema = mongoose.Schema;
3 |
4 | var flexBudgetSchema = new Schema({
5 | name: String,
6 | type: String,
7 | cost: {
8 | type: Number,
9 | default: undefined
10 | },
11 | costPerUnit: {
12 | type: Number,
13 | default: undefined
14 | },
15 | user: {
16 | type: mongoose.Schema.Types.ObjectId,
17 | ref: "User",
18 | required: true,
19 | trim: true
20 | }
21 | });
22 |
23 | module.exports = mongoose.model('flexBudget', flexBudgetSchema);
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "accounts-app",
3 | "version": "1.0.0",
4 | "description": "An accounting API",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "nodemon src/app.js"
8 | },
9 | "author": "Deepak Sharma",
10 | "license": "ISC",
11 | "dependencies": {
12 | "bcrypt": "^5.0.0",
13 | "connect-mongo": "^3.2.0",
14 | "express": "^4.17.1",
15 | "express-session": "^1.17.1",
16 | "express-validator": "^6.9.2",
17 | "helmet": "^4.3.1",
18 | "moment": "^2.29.1",
19 | "mongoose": "^5.11.12",
20 | "validator": "^13.5.2"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/.github/workflows/nodejs.yml:
--------------------------------------------------------------------------------
1 | name: Node CI
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 |
8 | runs-on: ubuntu-latest
9 |
10 | strategy:
11 | matrix:
12 | node-version: [8.x, 10.x, 12.x]
13 |
14 | steps:
15 | - uses: actions/checkout@v1
16 | - name: Use Node.js ${{ matrix.node-version }}
17 | uses: actions/setup-node@v1
18 | with:
19 | node-version: ${{ matrix.node-version }}
20 | - name: npm install, build, and test
21 | run: |
22 | npm install
23 | npm run build --if-present
24 | npm test
25 | env:
26 | CI: true
27 |
--------------------------------------------------------------------------------
/src/models/trialModel.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose")
2 |
3 | const trialSchema = new mongoose.Schema({
4 | creditBalance: {
5 | type: Number,
6 | required: true
7 | },
8 | debitBalance: {
9 | type: Number,
10 | required: true
11 | },
12 | ledger: [{
13 | account: {
14 | name: {
15 | type: String
16 | }
17 | },
18 | balance: {
19 | type: {
20 | type: String,
21 | default: undefined
22 | },
23 | amount: {
24 | type: Number,
25 | default: 0
26 | }
27 | }
28 | }],
29 | user: {
30 | type: mongoose.Schema.Types.ObjectId,
31 | ref: "User"
32 | }
33 | })
34 |
35 | const Trial = mongoose.model("Trial", trialSchema)
36 |
37 | module.exports = Trial
--------------------------------------------------------------------------------
/src/models/budgetModel.js:
--------------------------------------------------------------------------------
1 | var mongoose = require('mongoose');
2 | var Schema = mongoose.Schema;
3 |
4 | var BudgetSchema = new Schema({
5 | payementOfFixedAssets: String,
6 | payementToCreditors: String,
7 | recievedOfDebators: String,
8 | payementOfExpenses: String,
9 | payementOfDividend: String,
10 | saleOfInvestments: String,
11 | saleOfFixedAsset: String,
12 | payementOfBonus: String,
13 | payementOfWages: String,
14 | clossingStock: String,
15 | payementOfTax: String,
16 | openingStock: String,
17 | saleOfShares: String,
18 | budgetMonth: String,
19 | cashSales: String,
20 | dividends: String,
21 | interest: String,
22 | borrowing: String
23 | });
24 |
25 | module.exports = mongoose.model('Budget', BudgetSchema);
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/src/models/inventoryModel.js:
--------------------------------------------------------------------------------
1 | var mongoose = require('mongoose');
2 | var Schema = mongoose.Schema;
3 |
4 | var InventorySchema = new Schema({
5 | name: {
6 | type: String,
7 | required: true,
8 | trim: true
9 | },
10 | category: {
11 | type: String,
12 | required: true,
13 | trim: true
14 | },
15 | quantity:{
16 | type: Number,
17 | default: 0
18 | },
19 | cost: {
20 | type: Number,
21 | default: 0
22 | },
23 | expiry: {
24 | type: Date
25 | },
26 | thresholdQuantity: {
27 | type: Number,
28 | default: 0
29 | },
30 | user: {
31 | type: mongoose.Schema.Types.ObjectId,
32 | ref: "User",
33 | required: true,
34 | trim: true
35 | }
36 | });
37 |
38 | module.exports = mongoose.model('Inventory', InventorySchema);
--------------------------------------------------------------------------------
/src/models/journalModel.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose")
2 |
3 | const JournalSchema = new mongoose.Schema({
4 | from: {
5 | type: mongoose.Schema.Types.ObjectId,
6 | required: true,
7 | ref: "Account"
8 | },
9 | to: {
10 | type: mongoose.Schema.Types.ObjectId,
11 | required: true,
12 | ref: "Account"
13 | },
14 | date: {
15 | type: Date,
16 | required: true
17 | },
18 | debit: {
19 | type: Number,
20 | required: true
21 | },
22 | credit: {
23 | type: Number,
24 | required: true
25 | },
26 | narration: [{
27 | type: String,
28 | trim: true
29 | }],
30 | user: {
31 | type: mongoose.Schema.Types.ObjectId,
32 | ref: "User"
33 | },
34 | addedToLedger: {
35 | type: Boolean,
36 | default: false
37 | }
38 | }, {
39 | timestamps: true
40 | })
41 |
42 | const Journal = mongoose.model("Journal", JournalSchema)
43 |
44 | module.exports = Journal
--------------------------------------------------------------------------------
/src/models/ledgerModel.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose")
2 |
3 | const ledgerSchema = new mongoose.Schema({
4 | account: {
5 | type: mongoose.Schema.Types.ObjectId,
6 | required: true,
7 | unique: true,
8 | ref: "Account"
9 | },
10 | debits: [{
11 | _id: false,
12 | to: {
13 | _id: false,
14 | type: mongoose.Schema.Types.ObjectId,
15 | ref: "Account"
16 | },
17 | amount: {
18 | type: Number
19 | }
20 | }],
21 | credits: [{
22 | _id: false,
23 | from: {
24 | _id: false,
25 | type: mongoose.Schema.Types.ObjectId,
26 | ref: "Account"
27 | },
28 | amount: {
29 | type: Number
30 | }
31 | }],
32 | balance: {
33 | _id: false,
34 | type: {
35 | type: String,
36 | default: undefined
37 | },
38 | amount: {
39 | type: Number,
40 | default: 0
41 | }
42 | },
43 | user: {
44 | type: mongoose.Schema.Types.ObjectId,
45 | ref: "User"
46 | }
47 | })
48 |
49 | const Ledger = mongoose.model("Ledger", ledgerSchema)
50 |
51 | module.exports = Ledger
--------------------------------------------------------------------------------
/src/middleware/auth.js:
--------------------------------------------------------------------------------
1 | const User = require("../models/userModel")
2 |
3 | // Function to check whether the user is logged in
4 | async function isUserLoggedIn (req, res, next) {
5 | try {
6 | if (!(req.session && req.session.user)) {
7 | return res.status(401).send({
8 | error: "Unauthorized Access!"
9 | });
10 | }else {
11 | const user = await User.findOne({ _id : req.session.user._id })
12 | if(user) {
13 | next();
14 | } else {
15 | req.session.user = null;
16 | return res.status(401).send({
17 | error: "Unauthorized Access!"
18 | });
19 | }
20 | }
21 | } catch(e) {
22 | res.status(400).send({
23 | error: e
24 | })
25 | }
26 | }
27 |
28 |
29 | // Function to check whether the user is logged out
30 | function isUserLoggedOut (req, res, next) {
31 | if (req.session && req.session.user) {
32 | return res.status(200).send({
33 | message: "User already Logged In!"
34 | });
35 | }
36 | next();
37 | }
38 |
39 | module.exports = {
40 | isUserLoggedIn,
41 | isUserLoggedOut
42 | }
--------------------------------------------------------------------------------
/license:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Adhikansh Mittal
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/models/userModel.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose")
2 | const bcrypt = require("bcrypt")
3 |
4 | const userSchema = new mongoose.Schema({
5 | id: {
6 | type: String,
7 | unique: true,
8 | required: true,
9 | trim: true
10 | },
11 | password: {
12 | type: String,
13 | required: true,
14 | trim: true
15 | }
16 | })
17 |
18 | userSchema.methods.toJSON = function () {
19 | const user = this
20 |
21 | const userObject = user.toObject()
22 |
23 | delete userObject.password
24 |
25 | return userObject
26 | }
27 |
28 | userSchema.statics.authenticate = async (id, password) => {
29 | const user = await User.findOne({ id })
30 |
31 | if(!user){
32 | return null
33 | }
34 |
35 | const isMatch = await bcrypt.compare(password, user.password)
36 |
37 | if(!isMatch){
38 | return null
39 | }
40 |
41 | return user
42 | }
43 |
44 | userSchema.pre("save", async function(next) {
45 | const user = this
46 |
47 | if(user.isModified("password"))
48 | {
49 | user.password = await bcrypt.hash(user.password, 8)
50 | }
51 |
52 | next()
53 | })
54 |
55 | const User = mongoose.model("User", userSchema)
56 |
57 | module.exports = User
--------------------------------------------------------------------------------
/src/routes/trialRoute.js:
--------------------------------------------------------------------------------
1 | const express = require("express")
2 |
3 | const Ledger = require("../models/ledgerModel")
4 | const Trial = require("../models/trialModel")
5 | const { isUserLoggedIn } = require("../middleware/auth.js")
6 |
7 | const router = new express.Router()
8 |
9 | router.get("/", isUserLoggedIn, async (req, res) => {
10 | try{
11 | let ledger = await Ledger.find({ user: req.session.user._id }).select("account balance -_id")
12 | let creditBalance = 0
13 | let debitBalance = 0
14 |
15 | for (const account of ledger) {
16 | await account.populate("account", "name -_id").execPopulate()
17 |
18 | if(account.balance.type === "debit") {
19 | debitBalance += account.balance.amount
20 | } else if(account.balance.type === "credit"){
21 | creditBalance += account.balance.amount
22 | }
23 | }
24 |
25 | const newTrial = new Trial({
26 | creditBalance,
27 | debitBalance,
28 | ledger,
29 | user: req.session.user._id
30 | })
31 |
32 | await newTrial.save()
33 |
34 | res.status(200).send({
35 | creditBalance,
36 | debitBalance,
37 | ledger
38 | })
39 |
40 | } catch(e) {
41 | res.status(500).send({
42 | error: e
43 | })
44 | }
45 |
46 | })
47 |
48 | module.exports = router
49 |
--------------------------------------------------------------------------------
/src/app.js:
--------------------------------------------------------------------------------
1 | const helmet = require("helmet")
2 | const express = require("express")
3 | const db = require("./db/mongoose.js")
4 | const session = require("express-session")
5 | const MongoStore = require("connect-mongo")(session)
6 |
7 | const journalRoute = require("./routes/journalRoute")
8 | const ledgerRoute = require("./routes/ledgerRoute")
9 | const trialRoute = require("./routes/trialRoute")
10 | const finalRoute = require("./routes/finalAccRoute")
11 | const userRoute = require("./routes/userRoute")
12 | const budgetRoute = require("./routes/budgetRoute")
13 | const flexBudgetRoute = require("./routes/flexBudgetRoute")
14 | const inventoryRoute = require("./routes/inventoryRoute")
15 |
16 | const app = express()
17 |
18 | const port = process.env.PORT || 3000
19 |
20 | app.enable('trust proxy');
21 |
22 | app.use(helmet())
23 |
24 | app.use(express.urlencoded({ extended: true }))
25 |
26 | //setup express-session middleware
27 | app.use(session({
28 | secret: 'Xy12MIbneRt Weasd3',
29 | resave: true,
30 | saveUninitialized: true,
31 | cookie: {
32 | expires: new Date(Date.now() + 60 * 60 * 3000)
33 | },
34 | store: new MongoStore({
35 | mongooseConnection: db
36 | })
37 | }));
38 |
39 | app.get("/", (req, res) => {
40 | res.status(200).send({
41 | message: "Welcome!"
42 | })
43 | })
44 |
45 | app.use("/budget", budgetRoute)
46 | app.use("/flexBudget", flexBudgetRoute)
47 | app.use("/inventory", inventoryRoute)
48 | app.use("/journal", journalRoute)
49 | app.use("/ledger", ledgerRoute)
50 | app.use("/trial", trialRoute)
51 | app.use("/final", finalRoute)
52 | app.use("/user", userRoute)
53 |
54 | app.listen(port, () => {
55 | console.log(`Server started at port ${port}.`)
56 | })
57 |
--------------------------------------------------------------------------------
/src/routes/budgetRoute.js:
--------------------------------------------------------------------------------
1 | const express = require("express")
2 |
3 | const Budget = require("../models/budgetModel")
4 | const { isUserLoggedIn } = require("../middleware/auth.js")
5 |
6 | const router = new express.Router()
7 |
8 | router.route("/add").post(isUserLoggedIn, async function (req, res) {
9 |
10 | var budgetNew = new Budget();
11 | budgetNew.budgetMonth = req.body.budgetMonth;
12 | budgetNew.openingStock = req.body.openingStock;
13 | budgetNew.clossingStock = req.body.clossingStock;
14 | budgetNew.cashSales = req.body.cashSales;
15 | budgetNew.recievedOfDebators = req.body.recievedOfDebators;
16 | budgetNew.dividends = req.body.dividends;
17 | budgetNew.interest = req.body.interest;
18 | budgetNew.saleOfFixedAsset = req.body.saleOfFixedAsset;
19 | budgetNew.saleOfShares = req.body.saleOfShares;
20 | budgetNew.borrowing = req.body.borrowing;
21 | budgetNew.saleOfInvestments = req.body.saleOfInvestments;
22 | budgetNew.payementToCreditors = req.body.payementToCreditors;
23 | budgetNew.payementOfWages = req.body.payementOfWages;
24 | budgetNew.payementOfExpenses = req.body.payementOfExpenses;
25 | budgetNew.payementOfDividend = req.body.payementOfDividend;
26 | budgetNew.payementOfFixedAssets = req.body.payementOfFixedAssests;
27 | budgetNew.payementOfTax = req.body.payementOfTax;
28 | budgetNew.payementOfBonus = req.body.payementOfBonus;
29 |
30 | await budgetNew.save()
31 |
32 | res.send({ success: "Budget inserted successfully!"})
33 | });
34 |
35 | router.route("/list").get(isUserLoggedIn, async function (req, res) {
36 | const budgets = await Budget.find()
37 |
38 | res.send({ budgets })
39 | })
40 |
41 | module.exports = router
42 |
--------------------------------------------------------------------------------
/src/routes/finalAccRoute.js:
--------------------------------------------------------------------------------
1 | const express = require("express")
2 |
3 | const Ledger = require("../models/ledgerModel")
4 | const Trial = require("../models/trialModel")
5 | const { isUserLoggedIn } = require("../middleware/auth.js")
6 |
7 | const router = new express.Router()
8 |
9 | router.get("/pnl", isUserLoggedIn, async (req, res) => {
10 | try{
11 | let ledger = await Ledger.find({ user: req.session.user._id }).select("account balance -_id")
12 | let creditBalance = 0
13 | let debitBalance = 0
14 |
15 | for (const account of ledger) {
16 | await account.populate("account", "name -_id").execPopulate()
17 |
18 | if(account.balance.type === "debit") {
19 | debitBalance += account.balance.amount
20 | } else if(account.balance.type === "credit"){
21 | creditBalance += account.balance.amount
22 | }
23 | }
24 |
25 | let profit, loss
26 |
27 | if(creditBalance > debitBalance) {
28 | profit = creditBalance - debitBalance
29 | loss = 0
30 | } else if(debitBalance > creditBalance){
31 | loss = debitBalance - creditBalance
32 | profit = 0
33 | } else {
34 | profit = loss = 0
35 | }
36 |
37 | res.status(200).send({
38 | profit,
39 | loss,
40 | ledger
41 | })
42 |
43 | } catch(e) {
44 | res.status(500).send({
45 | error: e
46 | })
47 | }
48 |
49 | })
50 |
51 | router.get("/balance", isUserLoggedIn, async (req, res) => {
52 | try{
53 | let ledger = await Ledger.find({ user: req.session.user._id }).select("account balance -_id")
54 | let assets = [], liabilities = []
55 | let creditBalance = 0
56 | let debitBalance = 0
57 |
58 |
59 | for (const account of ledger) {
60 | await account.populate("account", "name -_id").execPopulate()
61 |
62 | if(account.balance.type === "debit") {
63 | assets.push(account)
64 | debitBalance += account.balance.amount
65 | } else if(account.balance.type === "credit"){
66 | liabilities.push(account)
67 | creditBalance += account.balance.amount
68 |
69 | }
70 | }
71 |
72 | let profit, loss
73 |
74 | if(creditBalance > debitBalance) {
75 | profit = creditBalance - debitBalance
76 | loss = 0
77 | } else if(debitBalance > creditBalance){
78 | loss = debitBalance - creditBalance
79 | profit = 0
80 | } else {
81 | profit = loss = 0
82 | }
83 |
84 | res.status(200).send({
85 | assets,
86 | liabilities,
87 | profit,
88 | loss
89 | })
90 |
91 | } catch(e) {
92 | res.status(500).send({
93 | error: e
94 | })
95 | }
96 |
97 | })
98 |
99 |
100 | module.exports = router
101 |
--------------------------------------------------------------------------------
/src/routes/userRoute.js:
--------------------------------------------------------------------------------
1 | const express = require("express")
2 | const validator = require("validator")
3 | const mongoose = require("mongoose")
4 |
5 | const User = require("../models/userModel")
6 | const { isUserLoggedOut, isUserLoggedIn } = require("../middleware/auth.js")
7 | const { check, validationResult } = require("express-validator/check");
8 |
9 | const router = new express.Router()
10 |
11 | router.post("/login", isUserLoggedOut, [
12 | check("id").not().isEmpty().withMessage("Please provide Id.").trim().escape(),
13 | check("password").not().isEmpty().withMessage("Please provide password.").trim().escape()
14 | ], async (req, res) => {
15 | try {
16 | const errors = validationResult(req)
17 |
18 | if(!errors.isEmpty()){
19 | return res.status(400).send({
20 | error: "Bad request!",
21 | message: errors.array()
22 | })
23 | }
24 |
25 | let id = req.body.id
26 | let password = req.body.password
27 |
28 | const user = await User.authenticate(id, password)
29 |
30 | if(!user){
31 | return res.status(400).send({
32 | error: "Invalid Id or password!"
33 | })
34 | }
35 |
36 | req.session.user = user
37 |
38 | res.status(200).send({
39 | success: "User Logged In!"
40 | })
41 |
42 | } catch(e) {
43 | res.status(400).send({
44 | message: "Unable to login!",
45 | error: e
46 | })
47 | }
48 | })
49 |
50 | router.post("/register", isUserLoggedOut, [
51 | check("id").not().isEmpty().withMessage("Please provide Id.").trim().escape(),
52 | check("password").not().isEmpty().withMessage("Please provide password.").trim().escape()
53 | ], async (req, res) => {
54 | try {
55 | const errors = validationResult(req)
56 |
57 | if(!errors.isEmpty()){
58 | return res.status(400).send({
59 | error: "Bad request!",
60 | message: errors.array()
61 | })
62 | }
63 |
64 | let id = req.body.id
65 | let password = req.body.password
66 |
67 | let user = await User.findOne({ id })
68 |
69 | if(user){
70 | return res.status(400).send({
71 | error: "User already exists!"
72 | })
73 | }
74 |
75 | user = new User()
76 |
77 | user.id = id
78 | user.password = password
79 |
80 | await user.save()
81 |
82 | req.session.user = user
83 |
84 | res.status(200).send({
85 | success: "User registered and Logged In!"
86 | })
87 |
88 | } catch(e) {
89 | res.status(400).send({
90 | message: "Unable to register!",
91 | error: e
92 | })
93 | }
94 | })
95 |
96 | router.post("/logout", isUserLoggedIn, async (req, res) => {
97 | try {
98 | req.session.destroy()
99 |
100 | res.status(200).send({
101 | success: "User Logged Out!"
102 | })
103 |
104 | } catch(e) {
105 | res.status(400).send({
106 | message: "Unable to logout!",
107 | error: e
108 | })
109 | }
110 | })
111 |
112 | module.exports = router
113 |
--------------------------------------------------------------------------------
/src/routes/flexBudgetRoute.js:
--------------------------------------------------------------------------------
1 | const express = require("express")
2 | const { check, validationResult } = require("express-validator/check");
3 |
4 | const flexBudget = require("../models/flexBudgetModel")
5 | const { isUserLoggedIn } = require("../middleware/auth.js")
6 |
7 | const router = new express.Router()
8 |
9 | router.route("/add").post(isUserLoggedIn, [
10 | check("name").not().isEmpty().withMessage("Please provide 'name'!").trim().escape(),
11 | check("type").not().isEmpty().withMessage("Please provide 'type'!").custom((value) => ["fixed", "variable"].includes(value)).withMessage("'type' can be 'fixed' or 'variable'!").trim().escape()
12 | ], async function (req, res) {
13 |
14 | const errors = validationResult(req)
15 |
16 | if(!errors.isEmpty()) {
17 | return res.status(400).send({
18 | error: "Bad request!",
19 | message: errors.array()
20 | })
21 | }
22 |
23 | let name = req.body.name.toLowerCase()
24 | let type = req.body.type.toLowerCase()
25 | let costPerUnit, cost
26 |
27 | const entry = await flexBudget.findOne({ name, type, user: req.session.user._id })
28 |
29 | if(entry) {
30 | return res.status(302).send({message: "Entry already exists!"})
31 | }
32 |
33 | if(type == "fixed") {
34 | if(Number(req.body.cost) && !req.body.costPerUnit) {
35 | cost = req.body.cost
36 | } else {
37 | return res.status(400).send({ message: "Fixed expenses can have only fixed cost!"})
38 | }
39 | } else if(type == "variable") {
40 | if(Number(req.body.costPerUnit) && !req.body.cost) {
41 | costPerUnit = req.body.costPerUnit
42 | } else {
43 | return res.status(400).send({ message: "Variable expenses can have only costPerUnit!"})
44 | }
45 | }
46 |
47 | var newEntry = new flexBudget({
48 | name,
49 | type,
50 | cost,
51 | costPerUnit,
52 | user: req.session.user._id
53 | });
54 |
55 | await newEntry.save()
56 |
57 | res.send({ success: "New expense inserted in flexible budget successfully!"})
58 | });
59 |
60 | router.route("/").post(isUserLoggedIn, [
61 | check("units").not().isEmpty().withMessage("Please provide 'units'!").custom((value) => Number(value)).withMessage("'units' can be a number only!").trim().escape()
62 | ], async function (req, res) {
63 |
64 | const errors = validationResult(req)
65 |
66 | if(!errors.isEmpty()) {
67 | return res.status(400).send({
68 | error: "Bad request!",
69 | message: errors.array()
70 | })
71 | }
72 |
73 | let units = Number(req.body.units)
74 | let totalCostPerUnit = 0, totalCost = 0, cPU, cost
75 |
76 | const entries = await flexBudget.find({ user: req.session.user._id }).select("-_id -__v -user")
77 |
78 | for (const entry of entries) {
79 | if(entry.type == "fixed") {
80 | totalCost += entry.cost
81 | cPU = entry.cost / units
82 | totalCostPerUnit += cPU
83 | } else if(entry.type == "variable") {
84 | totalCostPerUnit += entry.costPerUnit
85 | cost = entry.costPerUnit * units
86 | totalCost += cost
87 | }
88 | }
89 |
90 | res.send({ totalCost, totalCostPerUnit, entries })
91 | });
92 |
93 | module.exports = router
94 |
--------------------------------------------------------------------------------
/src/routes/ledgerRoute.js:
--------------------------------------------------------------------------------
1 | const express = require("express")
2 |
3 | const Ledger = require("../models/ledgerModel")
4 | const Journal = require("../models/journalModel")
5 | const { isUserLoggedIn } = require("../middleware/auth.js")
6 | const router = new express.Router()
7 |
8 | router.get("/", isUserLoggedIn, async (req, res) => {
9 | try{
10 |
11 | let ledger = await Ledger.find({ user: req.session.user._id }).select("account debits credits")
12 | let creditBalance = 0
13 | let debitBalance = 0
14 | let balance = {}
15 |
16 | for (const account of ledger) {
17 | await account.populate("account", "name -_id").execPopulate()
18 | await account.populate("debits.to", "name -_id").execPopulate()
19 | await account.populate("credits.from", "name -_id").execPopulate()
20 |
21 | for await (const entry of account.debits) {
22 | debitBalance += entry.amount
23 | }
24 |
25 | for await (const entry of account.credits) {
26 | creditBalance += entry.amount
27 | }
28 |
29 | account.balance = {
30 | type : (creditBalance > debitBalance) ? "credit" : "debit",
31 | amount: (creditBalance > debitBalance) ? creditBalance - debitBalance : debitBalance - creditBalance,
32 | }
33 |
34 | await account.save()
35 |
36 | delete account._doc._id
37 |
38 | creditBalance = debitBalance = 0
39 | }
40 |
41 |
42 |
43 | res.status(200).send({
44 | ledger
45 | })
46 |
47 | } catch(e) {
48 | res.status(500).send({
49 | error: e
50 | })
51 | }
52 |
53 | })
54 |
55 |
56 | router.get("/update", isUserLoggedIn, async (req, res) => {
57 | try{
58 | const journal = await Journal.find({ user: req.session.user._id, addedToLedger: false })
59 |
60 | let ledgerFrom, ledgerTo
61 |
62 | for (const entry of journal) {
63 | ledgerFrom = await Ledger.findOne({ account: entry.from._id, user: req.session.user._id })
64 |
65 | if(!ledgerFrom) {
66 | let newLedgerAcc = new Ledger({
67 | account: entry.from._id,
68 | user: req.session.user._id
69 | })
70 | await newLedgerAcc.save()
71 | ledgerFrom = newLedgerAcc
72 | }
73 |
74 | ledgerFrom.debits.push({
75 | record: entry._id,
76 | to: entry.to._id,
77 | amount: entry.debit
78 | })
79 |
80 | await ledgerFrom.save()
81 |
82 | let ledgerTo = await Ledger.findOne({ account: entry.to._id, user: req.session.user._id })
83 |
84 | if(!ledgerTo) {
85 | let newLedgerAcc = new Ledger({
86 | account: entry.to._id,
87 | user: req.session.user._id
88 | })
89 | await newLedgerAcc.save()
90 | ledgerTo = newLedgerAcc
91 | }
92 |
93 | ledgerTo.credits.push({
94 | record: entry._id,
95 | from: entry.from._id,
96 | amount: entry.credit
97 | })
98 |
99 | await ledgerTo.save()
100 |
101 | entry.addedToLedger = true
102 | await entry.save()
103 | }
104 |
105 | let ledger = await Ledger.find({ user: req.session.user._id }).select("account debits credits")
106 | let creditBalance = 0
107 | let debitBalance = 0
108 | let balance = {}
109 |
110 | for (const account of ledger) {
111 | await account.populate("account", "name -_id").execPopulate()
112 | await account.populate("debits.to", "name -_id").execPopulate()
113 | await account.populate("credits.from", "name -_id").execPopulate()
114 |
115 | for await (const entry of account.debits) {
116 | debitBalance += entry.amount
117 | }
118 |
119 | for await (const entry of account.credits) {
120 | creditBalance += entry.amount
121 | }
122 |
123 | account.balance = {
124 | type : (creditBalance > debitBalance) ? "credit" : "debit",
125 | amount: (creditBalance > debitBalance) ? creditBalance - debitBalance : debitBalance - creditBalance,
126 | }
127 |
128 | await account.save()
129 |
130 | delete account._doc._id
131 |
132 | creditBalance = debitBalance = 0
133 | }
134 |
135 |
136 |
137 | res.status(200).send({
138 | ledger
139 | })
140 |
141 | } catch(e) {
142 | res.status(500).send({
143 | error: e
144 | })
145 | }
146 |
147 | })
148 | module.exports = router
--------------------------------------------------------------------------------
/src/routes/inventoryRoute.js:
--------------------------------------------------------------------------------
1 | const express = require("express")
2 | const moment = require("moment")
3 | const { check, validationResult } = require("express-validator/check");
4 |
5 | const Inventory = require("../models/inventoryModel")
6 | const { isUserLoggedIn } = require("../middleware/auth.js")
7 |
8 | const router = new express.Router()
9 |
10 | router.route("/add").post(isUserLoggedIn, [
11 | check("name").not().isEmpty().withMessage("Please provide inventory 'name' !").trim().escape(),
12 | check("category").not().isEmpty().withMessage("Please provide 'category' !").trim().escape(),
13 | check("quantity").not().isEmpty().withMessage("Please provide 'quantity' !").custom((value) => Number(value)).withMessage("'quantity' must be a number!").trim().escape(),
14 | check("thresholdQuantity").not().isEmpty().withMessage("Please provide 'quantity' !").custom((value) => Number(value)).withMessage("'thresholdQuantity' must be a number!").trim().escape(),
15 | check("expiry").not().isEmpty().withMessage("Provide 'expiry' field!").trim(),
16 | check("cost").not().isEmpty().withMessage("Please provide 'cost' !").custom((value) => Number(value)).withMessage("'cost' must be a number!").trim().escape()
17 | ], async function (req, res) {
18 | try {
19 | const errors = validationResult(req)
20 |
21 | if(!errors.isEmpty()) {
22 | return res.status(400).send({
23 | error: "Bad request!",
24 | message: errors.array()
25 | })
26 | }
27 |
28 | let expiry = new Date(req.body.expiry)
29 |
30 | if(isNaN(expiry)) {
31 | return res.status(400).send({ message: "Bad request!", error: "Invalid expiry date, use format like '05 September 2019'"})
32 | }
33 |
34 | let name = req.body.name.toLowerCase()
35 | let category = req.body.category.toLowerCase()
36 | let quantity = Number(req.body.quantity)
37 | let thresholdQuantity = Number(req.body.thresholdQuantity)
38 | let cost = Number(req.body.cost)
39 | let user = req.session.user._id
40 |
41 | let item = await Inventory.findOne({ name, category, user })
42 |
43 | if(item) {
44 | return res.status(400).send({ message: "Item already present in the given category!"})
45 | }
46 |
47 | item = new Inventory({
48 | name,
49 | category,
50 | quantity,
51 | thresholdQuantity,
52 | expiry,
53 | cost,
54 | user
55 | })
56 |
57 | await item.save()
58 |
59 | item = item.toObject()
60 |
61 | item.expiry = item.expiry.toDateString()
62 |
63 | delete item._id
64 | delete item.user
65 | delete item.__v
66 |
67 | res.status(200).send({ message: "Item added to inventory!", item })
68 |
69 | } catch(e) {
70 | res.status(400).send({ message: "Something went wrong!", error: e })
71 | }
72 | });
73 |
74 | router.route("/update").post(isUserLoggedIn, [
75 | check("name").not().isEmpty().withMessage("Please provide inventory 'name' !").trim().escape(),
76 | check("category").not().isEmpty().withMessage("Please provide 'category' !").trim().escape(),
77 | check("usedQuantity").not().isEmpty().withMessage("Please provide 'quantity' !").custom((value) => Number(value)).withMessage("'quantity' must be a number!").trim().escape()
78 | ], async function (req, res) {
79 | try {
80 | const errors = validationResult(req)
81 |
82 | if(!errors.isEmpty()) {
83 | return res.status(400).send({
84 | error: "Bad request!",
85 | message: errors.array()
86 | })
87 | }
88 |
89 | let name = req.body.name.toLowerCase()
90 | let category = req.body.category.toLowerCase()
91 | let usedQuantity = Number(req.body.usedQuantity)
92 | let user = req.session.user._id
93 |
94 | let item = await Inventory.findOne({ name, category, user })
95 |
96 | if(!item) {
97 | return res.status(404).send({ message: "No such item in inventory found!"})
98 | }
99 |
100 | if(usedQuantity > item.quantity) {
101 | return res.status(400).send({ message: "Used quantity is more than present quantity!", quantity: item.quantity, usedQuantity })
102 | }
103 |
104 | item.quantity -= usedQuantity
105 |
106 | await item.save()
107 |
108 | item = item.toObject()
109 |
110 | delete item._id
111 | delete item.user
112 | delete item.__v
113 |
114 | res.status(200).send({ message: "Item updated successfully!", item })
115 |
116 | } catch(e) {
117 | res.status(400).send({ message: "Something went wrong!", error: e })
118 | }
119 | res.send({ success: "Budget inserted successfully!"})
120 | });
121 |
122 | router.route("/").get(isUserLoggedIn, async function (req, res) {
123 | try{
124 | const inventory = await Inventory.find({ user: req.session.user._id }).select("-user -__v -_id")
125 |
126 | for (const item of inventory) {
127 | item.expiry = item.expiry.toDateString()
128 | }
129 |
130 | res.status(200).send({ inventory })
131 | } catch(e) {
132 | res.status(500).send({ message: "Something went wrong!", error: e })
133 | }
134 | })
135 |
136 | module.exports = router
137 |
--------------------------------------------------------------------------------
/src/routes/journalRoute.js:
--------------------------------------------------------------------------------
1 | const express = require("express")
2 | const validator = require("validator")
3 | const moment = require("moment")
4 | const mongoose = require("mongoose")
5 |
6 | const Journal = require("../models/journalModel")
7 | const Ledger = require("../models/ledgerModel")
8 | const Account = require("../models/accountModel")
9 | const { isUserLoggedIn } = require("../middleware/auth.js")
10 | const { check, validationResult } = require("express-validator/check");
11 |
12 | const router = new express.Router()
13 |
14 | router.get("/", isUserLoggedIn, async (req, res) => {
15 | try{
16 | let journal = await Journal.find({ user: req.session.user._id }).sort({ date: -1})
17 |
18 | let journalData = []
19 |
20 | for (const record of journal) {
21 | await record.populate("from", "name").execPopulate()
22 | await record.populate("to", "name").execPopulate()
23 |
24 | journalData.push({
25 | from: record.from.name,
26 | to: record.to.name,
27 | date: record.date.toDateString(),
28 | debit: record.debit,
29 | credit: record.credit,
30 | narration: record.narration
31 | })
32 | }
33 |
34 | res.status(200).send({
35 | journalData
36 | })
37 |
38 | } catch(e) {
39 | res.status(500).send({
40 | error: e
41 | })
42 | }
43 |
44 | })
45 |
46 | router.post("/add", isUserLoggedIn, [
47 | check("from").not().isEmpty().withMessage("Provide 'from' field!").isAlpha().withMessage("'from' field must be a name!").trim().escape(),
48 | check("to").not().isEmpty().withMessage("Provide 'to' field!").isAlpha().withMessage("'to' field must be a name!").trim().escape(),
49 | check("date").not().isEmpty().withMessage("Provide 'date' field!").custom((value) => moment(value, "MM/DD/YYYY", true).isValid()).withMessage("'date' must be in 'MM/DD/YYYY' format!").trim() ,
50 | check("debit").not().isEmpty().withMessage("Provide 'debit' field!").custom((value) => Number(value)).withMessage("'debit' must be a number!").trim().escape(),
51 | check("credit").not().isEmpty().withMessage("Provide 'credit' field!").custom((value) => Number(value)).withMessage("'credit' must be a number!").trim().escape(),
52 | check("narration").not().isEmpty().withMessage("Provide 'narration' field!").trim().escape()
53 | ], async (req, res) => {
54 | try{
55 | const errors = validationResult(req)
56 |
57 | if(!errors.isEmpty()) {
58 | return res.status(400).send({
59 | error: "Bad request!",
60 | message: errors.array()
61 | })
62 | }
63 |
64 | let from = req.body.from.toLowerCase()
65 | let to = req.body.to.toLowerCase()
66 | let date = new Date(req.body.date)
67 | let debit = req.body.debit
68 | let credit = req.body.credit
69 | let narration = req.body.narration
70 |
71 | const fromAccount = await Account.findOne({ name: from, user: req.session.user._id })
72 |
73 | if(!fromAccount) {
74 | let newAccount = new Account({
75 | name: from,
76 | user: req.session.user._id
77 | })
78 | await newAccount.save()
79 | from = newAccount
80 | } else {
81 | from = fromAccount
82 | }
83 |
84 | const toAccount = await Account.findOne({ name: to, user: req.session.user._id })
85 |
86 | if(!toAccount) {
87 | let newAccount = new Account({
88 | name: to,
89 | user: req.session.user._id
90 | })
91 | await newAccount.save()
92 | to = newAccount
93 | } else {
94 | to = toAccount
95 | }
96 |
97 | let newEntry = new Journal({
98 | from: from._id,
99 | to: to._id,
100 | date,
101 | debit,
102 | credit,
103 | narration: [narration + ` (Amount = ${debit} on date = ${date.toDateString()})`],
104 | user: req.session.user._id
105 | })
106 |
107 | await newEntry.save()
108 |
109 | res.status(200).send({
110 | message: "Entry added to Journal!",
111 | entry: {
112 | from: from.name,
113 | to: to.name,
114 | date: newEntry.date.toDateString(),
115 | debit: newEntry.debit,
116 | credit: newEntry.credit,
117 | narration: newEntry.narration
118 | }
119 | })
120 |
121 | } catch(e) {
122 | res.status(500).send({
123 | error: e
124 | })
125 | }
126 | })
127 |
128 | router.post("/update", isUserLoggedIn, [
129 | check("from").not().isEmpty().withMessage("Provide 'from' field!").isAlpha().withMessage("'from' field must be a name!").trim().escape(),
130 | check("to").not().isEmpty().withMessage("Provide 'to' field!").isAlpha().withMessage("'to' field must be a name!").trim().escape(),
131 | check("entryDate").not().isEmpty().withMessage("Provide 'entryDate' field!").custom((value) => moment(value, "MM/DD/YYYY", true).isValid()).withMessage("'entryDate' must be in 'MM/DD/YYYY' format!").trim(),
132 | check("updateDate").not().isEmpty().withMessage("Provide 'updateDate' field!").custom((value) => moment(value, "MM/DD/YYYY", true).isValid()).withMessage("'updateDate' must be in 'MM/DD/YYYY' format!").trim(),
133 | check("action").not().isEmpty().withMessage("Provide 'action' field!").custom((value) => ["increase", "decrease"].includes(value)).withMessage("'action' must be a either 'decrease', 'increase'!").trim().escape(),
134 | check("amount").not().isEmpty().withMessage("Provide 'amount' field!").custom((value) => Number(value)).withMessage("'amount' must be a number!").trim().escape(),
135 | check("narration").not().isEmpty().withMessage("Provide 'narration' field!").trim().escape()
136 | ], async (req, res) => {
137 | try{
138 | const errors = validationResult(req)
139 |
140 | if(!errors.isEmpty()) {
141 | return res.status(400).send({
142 | error: "Bad request!",
143 | message: errors.array()
144 | })
145 | }
146 |
147 | let from = req.body.from.toLowerCase()
148 | let to = req.body.to.toLowerCase()
149 | let entryDate = new Date(req.body.entryDate)
150 | let updateDate = new Date(req.body.updateDate)
151 | let action = req.body.action
152 | let amount = Number(req.body.amount)
153 | let narration = req.body.narration
154 |
155 |
156 | const fromAccount = await Account.findOne({ name: from, user: req.session.user._id })
157 |
158 | if(!fromAccount) {
159 | return res.status(404).send({
160 | error: "From account does not exisit!"
161 | })
162 | }
163 |
164 | const toAccount = await Account.findOne({ name: to, user: req.session.user._id })
165 |
166 | if(!toAccount) {
167 | return res.status(404).send({
168 | error: "To account does not exisit!"
169 | })
170 | }
171 |
172 | const entry = await Journal.findOne({ from: fromAccount._id , to: toAccount._id , date: entryDate.toDateString(), user: req.session.user._id })
173 |
174 | if(!entry) {
175 | return res.status(404).send({
176 | error: "Journal entry does not exisit!"
177 | })
178 | }
179 |
180 | if(action === "increase") {
181 | entry.debit = entry.credit = entry.debit + amount
182 | } else if(action === "decrease"){
183 | entry.debit = entry.credit = entry.debit - amount
184 | }
185 |
186 | narration = `${narration} Amount = ${amount} ${action} on date = ${updateDate.toDateString()}`
187 |
188 | entry.narration.push(narration)
189 |
190 | await entry.save()
191 |
192 | res.status(200).send({
193 | message: "Updated Journal Entry!",
194 | entry: {
195 | from: from.name,
196 | to: to.name,
197 | date: entry.date.toDateString(),
198 | debit: entry.debit,
199 | credit: entry.credit,
200 | narration: entry.narration
201 | }
202 | })
203 |
204 | } catch(e) {
205 | res.status(500).send({
206 | error: e
207 | })
208 | }
209 | })
210 |
211 |
212 |
213 | module.exports = router
214 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Nexus Accounts [](http://hits.dwyl.io/HrithikMittal/HrithikMittal/nexus-account)
2 | [](https://img.shields.io/github/license/HrithikMittal/Nexus-Account.svg)
3 |
4 | Nexus Account is an API which can be used to perform different accounting tasks such as creating Journal, Ledger, Trial Balance, Profit & Loss Account and Balance Sheet. It is also useful to create Flexible Budget or an Inventory.
5 |
6 | ### Prerequisites
7 |
8 | To work with the api you must have to install the following:
9 | * [NodeJS](https://nodejs.org/en/download/) - Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.
10 | * [MongoDB Server](https://docs.mongodb.com/manual/installation/) - NoSql Database and server
11 | * [Postman](https://www.getpostman.com/downloads/) - API development environment
12 |
13 | ## Installation
14 |
15 | Before doing anything you have to clone or download(and unzip) the project folder, open terminal and navigate to the project folder and run:
16 |
17 | ```bash
18 | npm install
19 | ```
20 | This will install all the dependencies required by the project.
21 |
22 | ## Getting Started
23 |
24 | To start using this API, start your local database server, open terminal and navigate to the project folder and run:
25 | ```bash
26 | npm run start
27 | ```
28 | If an error occur, check your database server or check if you have installed the prerequisites correctly.
29 |
30 | If there was no error, open Postman and create and send a new get request to:
31 |
32 | ```
33 | http://localhost:3000/
34 | ```
35 | Expected Output:
36 | ```
37 | {
38 | message: "Welcome!"
39 | }
40 | ```
41 |
42 | Firstly, you have to create a new user for working with the API.
43 |
44 | Send a post request to:
45 |
46 | ```
47 | http://localhost:3000/user/register
48 | ```
49 | Along with 'id' and 'password' field in the 'x-www-form-urlencoded' option for the body of the request in postman:
50 |
51 |
52 | | id |
53 | demouserid |
54 |
55 |
56 | | password |
57 | demo |
58 |
59 |
60 |
61 | Expected Output:
62 | ```
63 | {
64 | "success": "User registered and Logged In!"
65 | }
66 | ```
67 |
68 | Once you get this message, you are ready to work with the api.
69 | ## Routes
70 |
71 | Url for all these routes is ```http://localhost:3000``` and parameters for POST request are sent through 'x-www-form-urlencoded' method.
72 |
73 | ### User Routes
74 |
75 |
76 |
77 | | URL |
78 | Parameters |
79 | Method |
80 | Description |
81 |
82 |
83 | | /user/register
84 | |
85 |
86 | - id
87 | - password
88 |
89 | |
90 |
91 | POST
92 | |
93 |
94 | Register a user
95 | |
96 |
97 |
98 | | /user/login
99 | |
100 |
101 | - id
102 | - password
103 |
104 | |
105 |
106 | POST
107 | |
108 |
109 | Login a user
110 | |
111 |
112 |
113 | | /user/logout
114 | |
115 | |
119 |
120 | POST
121 | |
122 |
123 | Logout a user
124 | |
125 |
126 |
127 |
128 | ### Accounting Route
129 |
130 |
131 |
132 | | URL |
133 | Parameters |
134 | Method |
135 | Description |
136 |
137 |
138 | | /journal/add
139 | |
140 |
141 | - from
142 | - to
143 | - date (MM/DD/YYYY)
144 | - debit
145 | - credit
146 | - narration
147 |
148 | |
149 |
150 | POST
151 | |
152 |
153 | Add new entry into journal
154 | |
155 |
156 |
157 | | /journal
158 | |
159 |
162 | |
163 |
164 | GET
165 | |
166 |
167 | Get all journal entry
168 | |
169 |
170 |
171 | | /journal/update
172 | |
173 |
174 | - from
175 | - to
176 | - entryDate (MM/DD/YYYY)
177 | - updateDate (MM/DD/YYYY)
178 | - amount
179 | - narration
180 | - action ('increase' or 'decrease')
181 | |
183 |
184 | POST
185 | |
186 |
187 | Update a journal entry
188 | |
189 |
190 |
191 | | /ledger/update
192 | |
193 | |
197 |
198 | GET
199 | |
200 |
201 | Update the ledger
202 | |
203 |
204 |
205 | | /ledger
206 | |
207 | |
211 |
212 | GET
213 | |
214 |
215 | Get ledger
216 | |
217 |
218 |
219 | | /trial
220 | |
221 | |
225 |
226 | Get
227 | |
228 |
229 | Get trial balance
230 | |
231 |
232 |
233 | | /final/pnl
234 | |
235 | |
239 |
240 | GET
241 | |
242 |
243 | Get Profit and Loss Account
244 | |
245 |
246 |
247 | | /final/balance
248 | |
249 | |
253 |
254 | GET
255 | |
256 |
257 | Get Balance Sheet
258 | |
259 |
260 |
261 |
262 | ### Flexible Budget Route
263 |
264 |
265 |
266 | | URL |
267 | Parameters |
268 | Method |
269 | Description |
270 |
271 |
272 | | /flexBudget/add
273 | |
274 |
275 | - name
276 | - type ("fixed" or "variable")
277 | - cost (only if 'type' is "fixed")
278 | - costPerUnit (only if 'type' is "variable")
279 |
280 | |
281 |
282 | POST
283 | |
284 |
285 | Add an entry to budget
286 | |
287 |
288 |
289 | | /flexBudget
290 | |
291 |
292 | - units (Number)
293 |
294 | |
295 |
296 | POST
297 | |
298 |
299 | Get the flexible budget
300 | |
301 |
302 |
303 |
304 | ### Inventory Route
305 |
306 |
307 |
308 | | URL |
309 | Parameters |
310 | Method |
311 | Description |
312 |
313 |
314 | | /inventory/add
315 | |
316 |
317 | - name
318 | - category
319 | - quantity
320 | - thresholdQuantity
321 | - expiry (format '05 September 2019')
322 | - cost
323 |
324 | |
325 |
326 | POST
327 | |
328 |
329 | Add an item to inventory
330 | |
331 |
332 |
333 | | /inventory/update
334 | |
335 |
336 | - name
337 | - category
338 | - usedQuantity
339 |
340 | |
341 |
342 | POST
343 | |
344 |
345 | Update an item in inventory
346 | |
347 |
348 |
349 | | /inventory
350 | |
351 |
354 | |
355 |
356 | GET
357 | |
358 |
359 | Get the inventory
360 | |
361 |
362 |
363 |
364 | ### Repos for Seperate Access to the API's
365 |
379 |
380 | ### Authentication
381 | I used express-session to manage sessions to authenticate. We have isUserLoggedIn, isUserLoggedOut middleware function which checks if the user is authenticated or not. The session token is stored in the database using connect-mongo package and is deleted when the user logout
382 | ```
383 | async function isUserLoggedIn (req, res, next) {
384 | try {
385 | if (!(req.session && req.session.user)) {
386 | return res.status(401).send({
387 | error: "Unauthorized Access!"
388 | });
389 | }else {
390 | const user = await User.findOne({ _id : req.session.user._id })
391 | if(user) {
392 | next();
393 | } else {
394 | req.session.user = null;
395 | return res.status(401).send({
396 | error: "Unauthorized Access!"
397 | });
398 | }
399 | }
400 | } catch(e) {
401 | res.status(400).send({
402 | error: e
403 | })
404 | }
405 | }
406 |
407 |
408 | // Function to check whether the user is logged out
409 | function isUserLoggedOut (req, res, next) {
410 | if (req.session && req.session.user) {
411 | return res.status(200).send({
412 | message: "User already Logged In!"
413 | });
414 | }
415 | next();
416 | }
417 |
418 | module.exports = {
419 | isUserLoggedIn,
420 | isUserLoggedOut
421 | }
422 | ```
423 | Note: some of the APIs which are mentionted above are not authenticate so please remember to add it. So it will help to proctect the private routes.
424 |
425 | ## Deployment
426 |
427 | This api can be hosted on platform like heroku, aws, and others. MongoDB Atlas or Matlab can be used for remote database.
For instance, the application can be deployed on [Heroku](https://signup.heroku.com/login) by creating and registering an account. Following, create a new app and choose a deployment method (terminal or github) and follow the instruction there. Remote database can be created using Mongodb Atlas or Matlab.
For [Mongodb Atlas](https://cloud.mongodb.com/user?_ga=2.185306281.1809166196.1559570784-2125252051.1557828824#/atlas/register/accountProfile), you need to just to create your account and make a new cluster and link the cluster to your application through a URL. Following the given steps, you would have a remote application up and running.
428 |
429 | ## Contributing [](https://github.com/dwyl/esta/issues)
430 |
431 | If you are the helping and contributing one, your efforts and suggestion are always welcomed.
432 |
--------------------------------------------------------------------------------