Skip to content

Commit 1feeefd

Browse files
⚡ initial commit
0 parents  commit 1feeefd

File tree

11 files changed

+1208
-0
lines changed

11 files changed

+1208
-0
lines changed

.gitignore

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
pnpm-debug.log*
8+
lerna-debug.log*
9+
.env
10+
11+
node_modules
12+
dist
13+
dist-ssr
14+
*.local
15+
16+
# Editor directories and files
17+
.vscode/*
18+
!.vscode/extensions.json
19+
.idea
20+
.DS_Store
21+
*.suo
22+
*.ntvs*
23+
*.njsproj
24+
*.sln
25+
*.sw?

controllers/loanController.js

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
const Loan = require('../models/Loan');
2+
const { calculateEMISchedule } = require('../utils/loanCalculations');
3+
const { createCsvStringifier } = require('csv-writer');
4+
5+
exports.createLoan = async (req, res) => {
6+
try {
7+
const { userId, disbursementDate, amount, interestRate, tenure } = req.body;
8+
const emiSchedule = calculateEMISchedule(amount, interestRate, tenure, disbursementDate);
9+
10+
// Calculate total interest
11+
const totalInterest = emiSchedule.reduce((sum, emi) => sum + emi.interest, 0);
12+
13+
// Create loan with EMI schedule including outstanding balance
14+
const loan = new Loan({
15+
userId,
16+
disbursementDate,
17+
amount,
18+
interestRate,
19+
tenure,
20+
emiSchedule,
21+
totalInterest,
22+
totalAmount: amount + totalInterest,
23+
status: 'ACTIVE'
24+
});
25+
26+
await loan.save();
27+
res.status(201).json(loan);
28+
} catch (error) {
29+
res.status(500).json({ error: 'Server error' });
30+
}
31+
};
32+
33+
exports.getLoanLedger = async (req, res) => {
34+
try {
35+
const loan = await Loan.findById(req.params.id).populate('userId');
36+
if (!loan) return res.status(404).json({ error: 'Loan not found' });
37+
res.json(loan);
38+
} catch (error) {
39+
res.status(500).json({ error: 'Server error' });
40+
}
41+
};
42+
43+
exports.downloadLedger = async (req, res) => {
44+
try {
45+
const loan = await Loan.findById(req.params.id).populate('userId');
46+
if (!loan) return res.status(404).json({ error: 'Loan not found' });
47+
48+
// Create CSV content
49+
const headers = 'Due Date,EMI Amount,Principal,Interest,Outstanding Balance\n';
50+
51+
let remainingBalance = loan.amount;
52+
const rows = loan.emiSchedule.map(emi => {
53+
const dueDate = new Date(emi.dueDate).toLocaleDateString();
54+
remainingBalance -= emi.principal;
55+
return `${dueDate},${emi.amount},${emi.principal},${emi.interest},${Math.max(0, Math.round(remainingBalance))}`;
56+
}).join('\n');
57+
58+
const csvContent = headers + rows;
59+
60+
// Set headers for file download
61+
res.setHeader('Content-Type', 'text/csv');
62+
res.setHeader('Content-Disposition', `attachment; filename=loan-ledger-${loan._id}.csv`);
63+
res.setHeader('Access-Control-Expose-Headers', 'Content-Disposition');
64+
65+
// Send CSV content
66+
res.send(csvContent);
67+
} catch (error) {
68+
res.status(500).json({ error: 'Server error' });
69+
}
70+
};

controllers/userController.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
const User = require('../models/User');
2+
3+
exports.createUser = async (req, res) => {
4+
try {
5+
const user = new User(req.body);
6+
await user.save();
7+
res.status(201).json(user);
8+
} catch (error) {
9+
if (error.code === 11000) {
10+
return res.status(400).json({ error: 'Duplicate entry found' });
11+
}
12+
res.status(500).json({ error: 'Server error' });
13+
}
14+
};
15+
16+
exports.getUser = async (req, res) => {
17+
try {
18+
const user = await User.findById(req.params.id);
19+
if (!user) return res.status(404).json({ error: 'User not found' });
20+
res.json(user);
21+
} catch (error) {
22+
res.status(500).json({ error: 'Server error' });
23+
}
24+
};

models/Loan.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
const mongoose = require('mongoose');
2+
3+
const emiSchema = new mongoose.Schema({
4+
dueDate: Date,
5+
amount: Number,
6+
principal: Number,
7+
interest: Number,
8+
outstandingBalance: Number,
9+
isPaid: {
10+
type: Boolean,
11+
default: false
12+
}
13+
});
14+
15+
const loanSchema = new mongoose.Schema({
16+
userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
17+
disbursementDate: { type: Date, required: true },
18+
amount: { type: Number, required: true },
19+
interestRate: { type: Number, required: true },
20+
tenure: { type: Number, required: true },
21+
emiSchedule: [emiSchema],
22+
totalInterest: Number,
23+
totalAmount: Number,
24+
status: {
25+
type: String,
26+
enum: ['PENDING', 'ACTIVE', 'COMPLETED', 'DEFAULTED'],
27+
default: 'PENDING'
28+
}
29+
}, { timestamps: true });
30+
31+
module.exports = mongoose.model('Loan', loanSchema);

models/User.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
const mongoose = require('mongoose');
2+
3+
const userSchema = new mongoose.Schema({
4+
name: { type: String, required: true },
5+
dob: { type: Date, required: true },
6+
pan: { type: String, required: true, unique: true, uppercase: true },
7+
aadhaar: { type: String, required: true, unique: true },
8+
gstin: { type: String, required: true, unique: true },
9+
udyam: { type: String, required: true, unique: true }
10+
}, { timestamps: true });
11+
12+
module.exports = mongoose.model('User', userSchema);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy