Here's an implementation for a Financial Management system focusing on fee management, billing, payments, and financial report generation using Next.js, NestJS, and GraphQL.
Backend (NestJS)
1. Entities
Fee Entity:
// fee.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
import { User } from './user.entity';
@Entity()
export class Fee {
@PrimaryGeneratedColumn()
id: number;
@ManyToOne(() => User, (user) => user.fees)
user: User;
@Column()
amount: number;
@Column()
dueDate: Date;
@Column()
status: string; // Pending, Paid, Overdue
}
Payment Entity:
// payment.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne, CreateDateColumn } from 'typeorm';
import { Fee } from './fee.entity';
@Entity()
export class Payment {
@PrimaryGeneratedColumn()
id: number;
@ManyToOne(() => Fee, (fee) => fee.payments)
fee: Fee;
@Column()
amount: number;
@CreateDateColumn()
paymentDate: Date;
@Column()
method: string; // e.g., Credit Card, Bank Transfer
}
2. Services
Fee Service:
// fee.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Fee } from './fee.entity';
@Injectable()
export class FeeService {
constructor(
@InjectRepository(Fee)
private feeRepository: Repository<Fee>,
) {}
findAll(): Promise<Fee[]> {
return this.feeRepository.find({ relations: ['user'] });
}
findOne(id: number): Promise<Fee> {
return this.feeRepository.findOne(id, { relations: ['user'] });
}
create(userId: number, amount: number, dueDate: Date): Promise<Fee> {
const newFee = this.feeRepository.create({ user: { id: userId }, amount, dueDate, status: 'Pending' });
return this.feeRepository.save(newFee);
}
updateStatus(id: number, status: string): Promise<Fee> {
return this.feeRepository.save({ id, status });
}
}
Payment Service:
// payment.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Payment } from './payment.entity';
@Injectable()
export class PaymentService {
constructor(
@InjectRepository(Payment)
private paymentRepository: Repository<Payment>,
) {}
findAll(): Promise<Payment[]> {
return this.paymentRepository.find({ relations: ['fee'] });
}
create(feeId: number, amount: number, method: string): Promise<Payment> {
const newPayment = this.paymentRepository.create({ fee: { id: feeId }, amount, method });
return this.paymentRepository.save(newPayment);
}
}
3. Resolvers
Fee Resolver:
// fee.resolver.ts
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { FeeService } from './fee.service';
import { Fee } from './fee.entity';
@Resolver(() => Fee)
export class FeeResolver {
constructor(private feeService: FeeService) {}
@Query(() => [Fee])
async fees() {
return this.feeService.findAll();
}
@Mutation(() => Fee)
async createFee(
@Args('userId') userId: number,
@Args('amount') amount: number,
@Args('dueDate') dueDate: string,
) {
return this.feeService.create(userId, amount, new Date(dueDate));
}
@Mutation(() => Fee)
async updateFeeStatus(
@Args('id') id: number,
@Args('status') status: string,
) {
return this.feeService.updateStatus(id, status);
}
}
Payment Resolver:
// payment.resolver.ts
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { PaymentService } from './payment.service';
import { Payment } from './payment.entity';
@Resolver(() => Payment)
export class PaymentResolver {
constructor(private paymentService: PaymentService) {}
@Query(() => [Payment])
async payments() {
return this.paymentService.findAll();
}
@Mutation(() => Payment)
async createPayment(
@Args('feeId') feeId: number,
@Args('amount') amount: number,
@Args('method') method: string,
) {
return this.paymentService.create(feeId, amount, method);
}
}
Frontend (Next.js)
1. Apollo Client Setup
// apollo-client.js
import { ApolloClient, InMemoryCache } from '@apollo/client';
const client = new ApolloClient({
uri: 'http://localhost:3000/graphql',
cache: new InMemoryCache(),
});
export default client;
2. Fee Management Page
// pages/fees.js
import { useState } from 'react';
import { useQuery, useMutation, gql } from '@apollo/client';
const GET_FEES = gql`
query GetFees {
fees {
id
amount
dueDate
status
user {
username
}
}
}
`;
const CREATE_FEE = gql`
mutation CreateFee($userId: Int!, $amount: Float!, $dueDate: String!) {
createFee(userId: $userId, amount: $amount, dueDate: $dueDate) {
id
amount
dueDate
status
}
}
`;
const UPDATE_FEE_STATUS = gql`
mutation UpdateFeeStatus($id: Int!, $status: String!) {
updateFeeStatus(id: $id, status: $status) {
id
status
}
}
`;
export default function Fees() {
const { loading, error, data } = useQuery(GET_FEES);
const [createFee] = useMutation(CREATE_FEE);
const [updateFeeStatus] = useMutation(UPDATE_FEE_STATUS);
const [userId, setUserId] = useState('');
const [amount, setAmount] = useState('');
const [dueDate, setDueDate] = useState('');
const [feeId, setFeeId] = useState('');
const [status, setStatus] = useState('');
const handleCreateFee = async (e) => {
e.preventDefault();
await createFee({ variables: { userId: parseInt(userId), amount: parseFloat(amount), dueDate } });
setUserId('');
setAmount('');
setDueDate('');
};
const handleUpdateFeeStatus = async (e) => {
e.preventDefault();
await updateFeeStatus({ variables: { id: parseInt(feeId), status } });
setFeeId('');
setStatus('');
};
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>Fees</h1>
<form onSubmit={handleCreateFee}>
<input
type="number"
placeholder="User ID"
value={userId}
onChange={(e) => setUserId(e.target.value)}
/>
<input
type="number"
placeholder="Amount"
value={amount}
onChange={(e) => setAmount(e.target.value)}
/>
<input
type="date"
placeholder="Due Date"
value={dueDate}
onChange={(e) => setDueDate(e.target.value)}
/>
<button type="submit">Create Fee</button>
</form>
<form onSubmit={handleUpdateFeeStatus}>
<input
type="number"
placeholder="Fee ID"
value={feeId}
onChange={(e) => setFeeId(e.target.value)}
/>
<input
type="text"
placeholder="Status"
value={status}
onChange={(e) => setStatus(e.target.value)}
/>
<button type="submit">Update Fee Status</button>
</form>
<ul>
{data.fees.map((fee) => (
<li key={fee.id}>
User: {fee.user.username}, Amount: {fee.amount}, Due: {fee.dueDate}, Status: {fee.status}
</li>
))}
</ul>
</div>
);
}
3. Payment Management Page
// pages/payments.js
import { useState } from 'react';
import { useQuery, useMutation, gql } from '@apollo/client';
const GET_PAYMENTS = gql`
query GetPayments {
payments {
id
amount
paymentDate
method
fee {
amount
user {
username
}
}
}
}
`;
const CREATE_PAYMENT = gql`
mutation CreatePayment($feeId: Int!, $amount: Float!, $method: String!) {
createPayment(feeId: $feeId, amount
: $amount, method: $method) {
id
amount
paymentDate
method
}
}
`;
export default function Payments() {
const { loading, error, data } = useQuery(GET_PAYMENTS);
const [createPayment] = useMutation(CREATE_PAYMENT);
const [feeId, setFeeId] = useState('');
const [amount, setAmount] = useState('');
const [method, setMethod] = useState('');
const handleCreatePayment = async (e) => {
e.preventDefault();
await createPayment({ variables: { feeId: parseInt(feeId), amount: parseFloat(amount), method } });
setFeeId('');
setAmount('');
setMethod('');
};
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>Payments</h1>
<form onSubmit={handleCreatePayment}>
<input
type="number"
placeholder="Fee ID"
value={feeId}
onChange={(e) => setFeeId(e.target.value)}
/>
<input
type="number"
placeholder="Amount"
value={amount}
onChange={(e) => setAmount(e.target.value)}
/>
<input
type="text"
placeholder="Method"
value={method}
onChange={(e) => setMethod(e.target.value)}
/>
<button type="submit">Create Payment</button>
</form>
<ul>
{data.payments.map((payment) => (
<li key={payment.id}>
Fee: {payment.fee.amount}, User: {payment.fee.user.username}, Amount: {payment.amount}, Date: {payment.paymentDate}, Method: {payment.method}
</li>
))}
</ul>
</div>
);
}
Financial Reports (Next.js)
To generate financial reports, we can create a new page that aggregates data from fees and payments.
Financial Reports Page
// pages/financial-reports.js
import { useQuery, gql } from '@apollo/client';
const GET_FEES_AND_PAYMENTS = gql`
query GetFeesAndPayments {
fees {
id
amount
dueDate
status
user {
username
}
}
payments {
id
amount
paymentDate
method
fee {
amount
user {
username
}
}
}
}
`;
export default function FinancialReports() {
const { loading, error, data } = useQuery(GET_FEES_AND_PAYMENTS);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
const totalFees = data.fees.reduce((sum, fee) => sum + fee.amount, 0);
const totalPayments = data.payments.reduce((sum, payment) => sum + payment.amount, 0);
return (
<div>
<h1>Financial Reports</h1>
<h2>Total Fees: ${totalFees}</h2>
<h2>Total Payments: ${totalPayments}</h2>
<h3>Fees</h3>
<ul>
{data.fees.map((fee) => (
<li key={fee.id}>
User: {fee.user.username}, Amount: {fee.amount}, Due: {fee.dueDate}, Status: {fee.status}
</li>
))}
</ul>
<h3>Payments</h3>
<ul>
{data.payments.map((payment) => (
<li key={payment.id}>
Fee: {payment.fee.amount}, User: {payment.fee.user.username}, Amount: {payment.amount}, Date: {payment.paymentDate}, Method: {payment.method}
</li>
))}
</ul>
</div>
);
}
GraphQL Schema
Define your GraphQL schema to match the resolver functions:
type User {
id: ID!
username: String!
}
type Fee {
id: ID!
amount: Float!
dueDate: String!
status: String!
user: User!
}
type Payment {
id: ID!
amount: Float!
paymentDate: String!
method: String!
fee: Fee!
}
type Query {
fees: [Fee!]!
payments: [Payment!]!
}
type Mutation {
createFee(userId: Int!, amount: Float!, dueDate: String!): Fee!
updateFeeStatus(id: Int!, status: String!): Fee!
createPayment(feeId: Int!, amount: Float!, method: String!): Payment!
}
This setup covers the backend and frontend code for developing a fee management system with billing, payments, and financial report generation. You can expand on this by adding more features, such as detailed payment history, invoice generation, and more comprehensive financial reports.
Sure! Let's expand the existing system to include detailed payment history and invoice generation. This involves updating the backend to handle invoices and modifying the frontend to display detailed payment history and generate invoices.
Backend (NestJS)
1. Entities
Add a new Invoice
entity:
Invoice Entity:
// invoice.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne, CreateDateColumn } from 'typeorm';
import { User } from './user.entity';
import { Payment } from './payment.entity';
@Entity()
export class Invoice {
@PrimaryGeneratedColumn()
id: number;
@ManyToOne(() => User, (user) => user.invoices)
user: User;
@Column()
amount: number;
@CreateDateColumn()
generatedAt: Date;
@ManyToOne(() => Payment, (payment) => payment.invoice)
payment: Payment;
}
2. Services
Invoice Service:
// invoice.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Invoice } from './invoice.entity';
import { Payment } from './payment.entity';
import { User } from './user.entity';
@Injectable()
export class InvoiceService {
constructor(
@InjectRepository(Invoice)
private invoiceRepository: Repository<Invoice>,
) {}
findAll(): Promise<Invoice[]> {
return this.invoiceRepository.find({ relations: ['user', 'payment'] });
}
create(userId: number, paymentId: number, amount: number): Promise<Invoice> {
const newInvoice = this.invoiceRepository.create({
user: { id: userId },
payment: { id: paymentId },
amount,
});
return this.invoiceRepository.save(newInvoice);
}
}
3. Resolvers
Invoice Resolver:
// invoice.resolver.ts
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { InvoiceService } from './invoice.service';
import { Invoice } from './invoice.entity';
@Resolver(() => Invoice)
export class InvoiceResolver {
constructor(private invoiceService: InvoiceService) {}
@Query(() => [Invoice])
async invoices() {
return this.invoiceService.findAll();
}
@Mutation(() => Invoice)
async createInvoice(
@Args('userId') userId: number,
@Args('paymentId') paymentId: number,
@Args('amount') amount: number,
) {
return this.invoiceService.create(userId, paymentId, amount);
}
}
4. Update GraphQL Schema
Update your GraphQL schema to include the new Invoice
type and related queries and mutations:
type User {
id: ID!
username: String!
invoices: [Invoice!]!
}
type Fee {
id: ID!
amount: Float!
dueDate: String!
status: String!
user: User!
}
type Payment {
id: ID!
amount: Float!
paymentDate: String!
method: String!
fee: Fee!
invoice: Invoice
}
type Invoice {
id: ID!
amount: Float!
generatedAt: String!
user: User!
payment: Payment!
}
type Query {
fees: [Fee!]!
payments: [Payment!]!
invoices: [Invoice!]!
}
type Mutation {
createFee(userId: Int!, amount: Float!, dueDate: String!): Fee!
updateFeeStatus(id: Int!, status: String!): Fee!
createPayment(feeId: Int!, amount: Float!, method: String!): Payment!
createInvoice(userId: Int!, paymentId: Int!, amount: Float!): Invoice!
}
Frontend (Next.js)
1. Invoice Management Page
pages/invoices.js
import { useState } from 'react';
import { useQuery, useMutation, gql } from '@apollo/client';
const GET_INVOICES = gql`
query GetInvoices {
invoices {
id
amount
generatedAt
user {
username
}
payment {
id
amount
}
}
}
`;
const CREATE_INVOICE = gql`
mutation CreateInvoice($userId: Int!, $paymentId: Int!, $amount: Float!) {
createInvoice(userId: $userId, paymentId: $paymentId, amount: $amount) {
id
amount
generatedAt
}
}
`;
export default function Invoices() {
const { loading, error, data } = useQuery(GET_INVOICES);
const [createInvoice] = useMutation(CREATE_INVOICE);
const [userId, setUserId] = useState('');
const [paymentId, setPaymentId] = useState('');
const [amount, setAmount] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
await createInvoice({ variables: { userId: parseInt(userId), paymentId: parseInt(paymentId), amount: parseFloat(amount) } });
setUserId('');
setPaymentId('');
setAmount('');
};
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>Invoices</h1>
<form onSubmit={handleSubmit}>
<input
type="number"
placeholder="User ID"
value={userId}
onChange={(e) => setUserId(e.target.value)}
/>
<input
type="number"
placeholder="Payment ID"
value={paymentId}
onChange={(e) => setPaymentId(e.target.value)}
/>
<input
type="number"
placeholder="Amount"
value={amount}
onChange={(e) => setAmount(e.target.value)}
/>
<button type="submit">Create Invoice</button>
</form>
<ul>
{data.invoices.map((invoice) => (
<li key={invoice.id}>
User: {invoice.user.username}, Amount: {invoice.amount}, Generated At: {invoice.generatedAt}, Payment ID: {invoice.payment.id}
</li>
))}
</ul>
</div>
);
}
2. Detailed Payment History Page
pages/payment-history.js
import { useQuery, gql } from '@apollo/client';
const GET_PAYMENTS = gql`
query GetPayments {
payments {
id
amount
paymentDate
method
fee {
id
amount
user {
username
}
}
}
}
`;
export default function PaymentHistory() {
const { loading, error, data } = useQuery(GET_PAYMENTS);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>Payment History</h1>
<ul>
{data.payments.map((payment) => (
<li key={payment.id}>
User: {payment.fee.user.username}, Fee Amount: {payment.fee.amount}, Payment Amount: {payment.amount}, Date: {payment.paymentDate}, Method: {payment.method}
</li>
))}
</ul>
</div>
);
}
Integrating Payment Creation with Invoice Generation
To automatically generate an invoice when a payment is created, we can modify the PaymentService
and PaymentResolver
to include invoice generation.
payment.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Payment } from './payment.entity';
import { InvoiceService } from './invoice.service';
@Injectable()
export class PaymentService {
constructor(
@InjectRepository(Payment)
private paymentRepository: Repository<Payment>,
private invoiceService: InvoiceService,
) {}
async findAll(): Promise<Payment[]> {
return this.paymentRepository.find({ relations: ['fee', 'fee.user'] });
}
async create(feeId: number, amount: number, method: string): Promise<Payment> {
const newPayment = this.paymentRepository.create({ fee: { id: feeId }, amount, method });
const payment = await this.paymentRepository.save(newPayment);
await this.invoiceService.create(payment.fee.user.id, payment.id, amount);
return payment;
}
}
payment.resolver.ts
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { PaymentService } from './payment.service';
import { Payment } from './payment.entity';
@Resolver(() => Payment)
export class PaymentResolver {
constructor(private paymentService: PaymentService) {}
@Query(() => [Payment])
async payments() {
return this.paymentService.findAll();
}
@Mutation(() => Payment)
async createPayment(
@Args('feeId') feeId: number,
@Args('amount') amount: number,
@Args('method') method: string,
) {
return this.paymentService.create(feeId, amount, method);
}
}
This completes the implementation of detailed payment history and invoice generation within the financial management system. You can further enhance the system by adding features such as exporting invoices to PDF, sending invoice notifications via email, and more.
Support My Work โค๏ธ
If you enjoy my content and find it valuable, consider supporting me by buying me a coffee. Your support helps me continue creating and sharing useful resources. Thank you!
Connect with Me ๐
Letโs stay connected! You can follow me or reach out on these platforms:
๐น YouTube โ Tutorials, insights & tech content
๐น LinkedIn โ Professional updates & networking
๐น GitHub โ My open-source projects & contributions
๐น Instagram โ Behind-the-scenes & personal updates
๐น X (formerly Twitter) โ Quick thoughts & tech discussions
Iโd love to hear from youโwhether itโs feedback, collaboration ideas, or just a friendly hello!
Disclaimer
This content has been generated with the assistance of AI. While I strive for accuracy and quality, please verify critical information independently.
Top comments (1)
Great post! The detailed implementation of financial management systems is impressive. I was wondering how such systems could be extended to handle region-specific utilities, such as bill payments for FESCO (Faisalabad Electric Supply Company) in Pakistan (fesco-bill.pk/).
For instance, managing integrations with localized billing systems often involves handling unique APIs and currency formats. What challenges do you foresee in implementing such integrations, and how would you recommend ensuring smooth operations in financial management systems for these scenarios?"