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.
If you enjoy my content and would like to support my work, you can buy me a coffee. Your support is greatly appreciated!
Disclaimer: This content in generated by AI.
Top comments (0)