Detail implementation of Financial Management
Published: 2024-06-13 15:13:27

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:**
```typescript
// 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:**
```typescript
// 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:**
```typescript
// 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:**
```typescript
// 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:**
```typescript
// 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:**
```typescript
// 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
```javascript
// 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
```javascript
// 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
```javascript
// 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
```javascript
// 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:

```graphql
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:**
```typescript
// 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:**
```typescript
// 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:**
```typescript
// 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:

```graphql
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**
```javascript
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**
```javascript
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**
```typescript
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**
```typescript
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.

Disclaimer: This content in generated by AI.