Skip to main content

Initiate Payment

Overview

This feature is exclusively operated by Institute Admins — they upload the Excel file that generates payment requests for their students. Key capabilities include:

  • Bulk payment collection creation via Excel upload
  • Automatic student record creation and updates
  • Support for multiple fee types in a single collection
  • Flexible payment modes (Cash and Payment Gateway)
  • Automated email notifications to parents/guardians
  • Due date management with late fee calculations
  • Payment status tracking (Pending, Partially Paid, Fully Paid)
  • Excel template generation based on institute's configured fee types

Data Models

This section describes the data models you need to implement this feature, along with step-by-step instructions to create them in SolidX.

New to the Module Builder?

If you're unfamiliar with how modules, models, and fields work in SolidX, we recommend reviewing the Module Builder documentation first. It covers:

How These Models Connect

Understanding the relationships between your data:

Payment Collection (Batch: "Q1 2024 Fees")

├── Contains multiple Payment Collection Items
│ │
│ ├── Item 1: Rahul Sharma - Tuition Fees - ₹10,000
│ │ ├── Links to: Student (Rahul Sharma)
│ │ ├── Links to: Fee Type (Tuition Fees)
│ │ └── Has multiple Payment Collection Item Details
│ │ ├── Detail 1: Paid ₹5,000 on 2024-03-15
│ │ └── Detail 2: Paid ₹5,000 on 2024-04-10
│ │
│ ├── Item 2: Rahul Sharma - Bus Fees - ₹3,000
│ │ └── Links to: Fee Type (Bus Fees)
│ │
│ └── Item 3: Priya Patel - Tuition Fees - ₹10,000
│ └── Links to: Student (Priya Patel)

└── All items share same Payment Collection

Student (Rahul Sharma)
├── Has multiple Payment Collection Items across different collections
│ ├── From "Q1 2024 Fees" collection
│ ├── From "Annual Sports Fees" collection
│ └── From "Library Fees" collection

└── Belongs to one Institute

Fee Type (Tuition Fees)
├── Used in multiple Payment Collection Items
│ └── Different students, different collections

├── Defines: Part payment allowed?
└── Defines: Late fee calculation rules

Important Concepts

Automatic Student Management:

  • When you upload an Excel file, the system checks if each student already exists (by Student ID)
  • If the student exists: Updates their information with latest data from Excel
  • If the student is new: Creates a new student record automatically
  • You don't need to manually create students before uploading payment collections

Status Flow for Payment Gateway Items:

Upload Excel (PG mode) → Status: "Pending"

Student pays 50%

Status: "Partially Paid"

Student pays remaining

Status: "Fully Paid"

Status Flow for Cash Items:

Upload Excel (CASH mode) → Status: "Fully Paid"

No further action needed

Late Fee Calculation:

  • Runs automatically via scheduled job
  • Only applies to items with status "Pending" or "Partially Paid"
  • Calculation method comes from Fee Type configuration:
    • Percent: (pending amount × late fee %) ÷ 100
    • Absolute: Fixed late fee amount
    • None: No late fees applied
  • Updates total amount to be paid automatically

1. Student Model

Individual students enrolled at your institution whose parents/guardians will receive payment collection requests.

Model Configuration
SettingValue
Singular Namestudent
Plural Namestudents
Display NameStudent
DescriptionModel to capture student information
Data Sourcedefault
Data Source Typepostgres
Table Namefees_portal_student
Enable Audit TrackingYes
Enable Soft DeleteNo
Draft Publish WorkflowNo
InternationalizationNo
Is Child ModelNo
Fields
#NameDisplay NameTypeReq?Key Config
1studentNameStudent NameShort TextYesAudit
2studentEmailAddressStudent Email AddressShort TextNoAudit
3studentMobileNumberStudent Mobile NumberShort TextNoAudit
4parentNameParent/Guardian NameShort TextYesAudit
5parentMobileNumberParent/Guardian Mobile NumberShort TextYesAudit
6parentEmailAddressParent/Guardian Email AddressShort TextYesAudit
7studentIdStudent IdShort TextYesIs User Key
Audit
8instituteInstituteRelationYesMany-to-One → institute (fees-portal)
Create Inverse: No
Cascade
Index
Audit
9studentLoginIdStudent Login IdComputedYesUnique
Type: string
Provider: AlphaNumExternalIdComputationProvider
Generates unique alphanumeric login ID from student name, length 5 (full config ↓)
Trigger: before-insert on student (fees-portal)
Audit
10otpOTPShort TextNo
11otpExpiresAtOTP Expires AtDatetimeNo
12tokenTokenLong TextNo

Students are automatically created (or updated) when you upload a payment collection Excel file — matched by Student ID. Students can log in to the portal using their studentLoginId and OTP.

Deferred relation

The payments (One-to-Many → payment) field is not added here because the payment model does not exist yet. It is auto-created when you add the student field on the Payment model in Student Payment Portal (field 7, Create Inverse: Yes).

studentLoginId — Full provider context JSON
{"dynamicFieldPrefix": "studentName", "length": 5}

2. Payment Collection Model

A batch of fee collection requests sent to multiple students at once (e.g., "Q1 2024 Fees", "Annual Sports Fees 2024").

Model Configuration
SettingValue
Singular NamepaymentCollection
Plural NamepaymentCollections
Display NamePayment Collection
DescriptionModel used to capture information about a payment collection. A payment collection is a batch of payments that are collected together.
Data Sourcedefault
Data Source Typepostgres
Table Namefees_portal_payment_collection
Enable Audit TrackingYes
Enable Soft DeleteNo
Draft Publish WorkflowNo
InternationalizationNo
Is Child ModelNo
Fields
#NameDisplay NameTypeReq?Key Config
1nameNameShort TextYesIndex
Is User Key
Audit
2descriptionDescriptionLong TextNo
3dueDateDue DateDatetimeYesAudit
4paymentCollectionIdPayment Collection IDComputedYesUnique
Index
Type: string
Provider: AlphaNumExternalIdComputationProvider
Generates unique alphanumeric ID from collection name, length 5 (full config ↓)
Trigger: before-insert on paymentCollection (fees-portal)
Audit
5instituteInstituteRelationYesMany-to-One → institute (fees-portal)
Create Inverse: No
Cascade
Index
Audit
6paymentFilePayment FileMedia (Single)Yesfile
max 5120 KB
default-filesystem

The paymentCollectionItems relation is added in Step 5 after the paymentCollectionItem model exists. Deleting a Payment Collection cascades to all its Payment Collection Items via that relation.

paymentCollectionId — Full provider context JSON
{"dynamicFieldPrefix": "name", "length": 5}

3. Payment Collection Item Model

An individual payment request for one student for one fee type within a collection (e.g., "Rahul Sharma needs to pay ₹10,000 for Tuition Fees").

Model Configuration
SettingValue
Singular NamepaymentCollectionItem
Plural NamepaymentCollectionItems
Display NamePayment Collection Item
DescriptionModel used to capture information about a payment collection item. A payment collection item is a single payment within a payment collection.
Data Sourcedefault
Data Source Typepostgres
Table Namefees_portal_payment_collection_item
Enable Audit TrackingYes
Enable Soft DeleteNo
Draft Publish WorkflowNo
InternationalizationNo
Is Child ModelNo
Fields
#NameDisplay NameTypeReq?Key Config
1dueDateDue DateDatetimeYesAudit
2partPaymentAllowedPart Payment AllowedBooleanYesDefault: false
Audit
3statusStatusShort TextYesDefault: Pending
Index
Audit
4amountPaidAmount PaidComputedYesType: Decimal
Provider: PaymentCollectionItemAmountProvider
Sums all successful payments for this item (provider context: {})
Trigger: after-update on paymentCollectionItemDetail
Audit
5amountPendingAmount PendingComputedYesType: Decimal
Provider: NoopsEntityComputedFieldProviderService
Placeholder — actual value is amountToBePaid - amountPaid (provider context: {})
Trigger: before-insert on paymentCollectionItemDetail
Audit
6isOverdueIs OverdueBooleanYesDefault: false
Audit
7overdueByDaysOverdue By DaysIntegerNoAudit
8lateAmountToBePaidLate Amount To Be PaidDecimalNoDefault: 0
Audit
9totalAmountToBePaidTotal Amount To Be PaidComputedNoType: Decimal
Provider: NoopsEntityComputedFieldProviderService
Placeholder — actual value is amountToBePaid + lateAmountToBePaid (provider context: {})
Trigger: before-insert on paymentCollectionItemDetail
Audit
10amountToBePaidAmount To Be PaidDecimalYesAudit
11modeModeShort TextYesCASH or PG (Payment Gateway)
Audit
12studentStudentRelationYesMany-to-One → student (fees-portal)
Create Inverse: No
Cascade
Index
Audit
13paymentCollectionPayment CollectionRelationYesMany-to-One → paymentCollection (fees-portal)
Inverse field: paymentCollectionItems
Create Inverse: Yes
Cascade
Index
Audit
14instituteInstituteRelationYesMany-to-One → institute (fees-portal)
Create Inverse: No
Cascade
Index
Audit
15feeTypeFee TypeRelationYesMany-to-One → feeType (fees-portal)
Create Inverse: No
Cascade
Index
Audit

Payment Modes: Choose PG when parents will pay online (initial status: Pending); choose CASH when payment has already been collected offline (initial status: Fully Paid). The paymentCollectionItemDetails relation is added in Step 5 after the paymentCollectionItemDetail model exists.


4. Payment Collection Item Detail Model

Individual payment transactions recorded against a payment collection item — tracks each installment or payment attempt.

Model Configuration
SettingValue
Singular NamepaymentCollectionItemDetail
Plural NamepaymentCollectionItemDetails
Display NamePayment Collection Item Detail
DescriptionModel used to capture individual payment transactions for a payment collection item
Data Sourcedefault
Data Source Typepostgres
Table Namefees_portal_payment_collection_item_detail
Enable Audit TrackingYes
Enable Soft DeleteNo
Draft Publish WorkflowNo
InternationalizationNo
Is Child ModelNo
Fields
#NameDisplay NameTypeReq?Key Config
1paymentDatePayment DateDatetimeYesAudit
2paymentStatusPayment StatusShort TextYesDefault: Pending
Audit
3amountPaidAmount PaidDecimalYesAudit
4instituteInstituteRelationYesMany-to-One → institute (fees-portal)
Create Inverse: No
Cascade
Index
Audit
5studentStudentRelationYesMany-to-One → student (fees-portal)
Create Inverse: No
Cascade
Index
Audit
6paymentCollectionItemPayment Collection ItemRelationYesMany-to-One → paymentCollectionItem (fees-portal)
Inverse field: paymentCollectionItemDetails
Create Inverse: Yes
Cascade
Index
Audit
Deferred relation

The payment (Many-to-One → payment) field is not added here because the payment model does not exist yet. It is auto-created when you add the paymentCollectionItemDetails field on the Payment model in Student Payment Portal (field 9, Create Inverse: Yes).


5. Adding Relation Fields

Now that all four models exist, return to the following models and add the deferred relation fields.

Why add these last?

SolidX requires the co-model to already exist before you can create a relation pointing to it. The two One-to-Many fields below reference models that were not yet created when their parent models were built.

Payment Collection — add field 7:

#NameDisplay NameTypeReq?Key Config
7paymentCollectionItemsPayment Collection ItemsRelationOne-to-Many → paymentCollectionItem (fees-portal)
Inverse field: paymentCollection
Create Inverse: Yes
Cascade

Payment Collection Item — add field 16:

#NameDisplay NameTypeReq?Key Config
16paymentCollectionItemDetailsPayment Collection Item DetailsRelationOne-to-Many → paymentCollectionItemDetail (fees-portal)
Inverse field: paymentCollectionItem
Create Inverse: Yes
Cascade

The payments field on Student and the payment field on Payment Collection Item Detail are handled automatically in Student Payment Portal when the payment model is created (via Create Inverse: Yes on fields 7 and 9 respectively).

Quick Reference

For a handy summary of field types and configuration options used in this tutorial, see:

For comprehensive documentation, refer to the Module Builder section.

Generating APIs and UI Components

Once you've created the data models, you'll need to generate the REST APIs and UI components.

Reference Documentation

📋 For detailed step-by-step instructions, see Generating APIs and UI Components

Creating Roles & Permissions

With the models and APIs in place, configure who can access them. Permissions are granted per model and map directly to the controller methods generated in the previous step.

Super Admin

Super Admin is granted all permissions by default. No role configuration needed.

Institute Admin

On the existing Institute Admin role, grant the following additional permissions:

ModelPermissions
StudentStudentController.create StudentController.insertMany StudentController.findOne StudentController.findMany StudentController.update StudentController.partialUpdate
Payment CollectionPaymentCollectionController.create PaymentCollectionController.findOne PaymentCollectionController.findMany PaymentCollectionController.update PaymentCollectionController.partialUpdate
Payment Collection ItemPaymentCollectionItemController.findOne PaymentCollectionItemController.findMany PaymentCollectionItemController.cancelMany
Payment Collection Item DetailPaymentCollectionItemDetailController.findOne PaymentCollectionItemDetailController.findMany

Why these permissions?

  • Student — Institute Admin needs to view student records and make manual corrections. Students are primarily created via Excel upload (insertMany) but can also be added individually. No delete, as student records should persist for payment history.
  • Payment Collection — Institute Admin creates and manages collection batches. No insertMany (collections are always created one at a time with a specific Excel file) and no delete (collections are permanent audit records).
  • Payment Collection Item — Items are generated automatically by the Excel upload; Institute Admin never creates them manually. cancelMany allows bulk-cancelling items when a collection needs to be voided.
  • Payment Collection Item Detail — Purely a read-only view for Institute Admin; details are recorded by the payment gateway callback process, not manually.
Reference Documentation

📋 For step-by-step instructions on editing a role and assigning permissions in SolidX, see Role Management.

Implementing Custom Business Logic

While SolidX auto-generates basic CRUD operations, the payment collection feature requires custom business logic for Excel processing, student management, and email notifications. This section explains the key customizations needed.

1. Excel Upload Validation

Before processing the uploaded Excel file, validation ensures data quality and prevents errors.

Implementation Location: Payment Collection Service

  • File: solid-api/src/fees-portal/services/payment-collection.service.ts
  • Method: feeTypeValidation()

What It Does:

  1. Excel Structure Validation

    • Reads the uploaded Excel file using the ExcelService's readExcelFromStreamNonStreaming() method
    • Identifies header row and extracts column names
    • Known columns: Student Name, Student Id, Parent/Guardian Name/Email/Mobile, Payment Mode
    • Fee type columns: Any column not in Known Columns and not containing "Due Date" or Payment Mode is treated as a fee type column
  2. Fee Type Validation

    • Extracts unique fee types from Excel headers
    • Queries the database for institute's configured Fee Types
    • Throws error if any fee type in Excel is not configured
    • Error message example: "Fee type 'Lab Fees' is not configured for your institute"
  3. Due Date Validation

    • Ensures all due dates are today or in the future
    • Rejects past due dates with detailed error messages
    • Supports both string format (yyyy-mm-dd) and Excel date types
    • Example error: "Row 5: Due date for 'Tuition Fees' cannot be in the past"
  4. Payment Mode Validation

    • Only "CASH" or "PG" (Payment Gateway) allowed
    • Defaults to "PG" if empty
    • Case-insensitive validation
    • Example error: "Row 3: Invalid payment mode 'CHECK'. Use 'CASH' or 'PG'"
  5. Email Validation

    • Parent/Guardian email must be lowercase
    • Email is mandatory
    • Basic format validation

Key Code Pattern:

import { ExcelService } from "@solidxai/core";
import { createReadStream } from 'fs';

@Injectable()
export class PaymentCollectionService {
constructor(
private readonly excelService: ExcelService,
// ... other dependencies
) {}

async feeTypeValidation(filePath: string, instituteId: number) {
// Create a readable stream from the file path
const fileStream = createReadStream(filePath);

// Read the entire Excel file using ExcelService
const { headers, rows } = await this.excelService.readExcelFromStreamNonStreaming(
fileStream,
{
hasHeaderRow: true,
worksheetIndex: 0
}
);

// Extract fee type columns from headers
const knownColumns = [
'Student Name', 'Student Id',
'Parent/Guardian Name', 'Parent/Guardian Email', 'Parent/Guardian Mobile',
'Payment Mode'
];

const feeTypeColumns = headers.filter(header =>
!knownColumns.includes(header) &&
!header.includes('Due Date')
);

const excelFeeTypes = [...new Set(feeTypeColumns)]; // Get unique fee types

// Validate fee types against database
const instituteFeeTypes = await this.getFeeTypesForInstitute(instituteId);
const invalidFeeTypes = excelFeeTypes.filter(
ft => !instituteFeeTypes.map(f => f.feeType).includes(ft)
);

if (invalidFeeTypes.length > 0) {
throw new Error(`Fee types not configured: ${invalidFeeTypes.join(', ')}`);
}

// Validate each row
const today = new Date();
today.setHours(0, 0, 0, 0);

for (let i = 0; i < rows.length; i++) {
const row = rows[i];
const rowNumber = i + 2; // +2 because Excel rows start at 1 and row 1 is headers

// Validate due dates for each fee type
for (const feeType of excelFeeTypes) {
const dueDateColumn = `${feeType} Due Date`;
const amountColumn = `${feeType} Amount`;

const amount = row[amountColumn];
const dueDate = row[dueDateColumn];

// Only validate if amount is provided
if (amount && parseFloat(amount) > 0) {
if (!dueDate) {
throw new Error(
`Row ${rowNumber}: Due date is required for '${feeType}' when amount is specified`
);
}

const dueDateObj = new Date(dueDate);
if (dueDateObj < today) {
throw new Error(
`Row ${rowNumber}: Due date for '${feeType}' cannot be in the past`
);
}
}
}

// Validate payment mode
const paymentMode = row['Payment Mode']?.toString().toUpperCase() || 'PG';
if (!['CASH', 'PG'].includes(paymentMode)) {
throw new Error(
`Row ${rowNumber}: Invalid payment mode '${row['Payment Mode']}'. Use 'CASH' or 'PG'`
);
}

// Validate required fields
if (!row['Parent/Guardian Email']) {
throw new Error(`Row ${rowNumber}: Parent/Guardian Email is required`);
}
}

return { headers, rows };
}
}

2. Excel Template Generation

To help users format their Excel correctly, SolidX provides template generation.

Implementation Location: Generate Fee Collection Report Service

  • File: solid-api/src/fees-portal/services/generate-fee-collection-report.service.ts
  • Endpoint: GET /api/generate-fee-reports?id={instituteId}

What It Does:

  1. Fetches institute's configured fee types from database
  2. Generates Excel with dynamic headers based on fee types
  3. Headers format:
    • Fixed columns: Student Name, Student Id, Parent/Guardian Name, Parent/Guardian Email, Parent/Guardian Mobile
    • Dynamic columns: For each fee type: {FeeType} Amount, {FeeType} Due Date
    • Last column: Payment Mode
  4. Includes sample row with today's date
  5. Returns downloadable Excel file

Template Structure Example:

Student NameStudent IdParent/Guardian NameParent/Guardian EmailParent/Guardian MobileTuition Fees AmountTuition Fees Due DateBus Fees AmountBus Fees Due DatePayment Mode
Rahul SharmaSTU001Mr. Rajesh Sharmarajesh@example.com9123456789100002024-04-3030002024-04-30PG

3. File Processing with TypeORM Event Subscriber

The core processing logic uses TypeORM's Event Subscriber pattern to automatically process Excel files after upload.

Implementation Location: Media Transaction Subscriber

  • File: solid-api/src/fees-portal/subscriber/media-transaction.subscriber.ts
  • Event: afterInsert on Media entity

How It Works:

When a file is uploaded for the Payment Collection model:

  1. TypeORM fires afterInsert event after Media record is saved
  2. Subscriber checks if media belongs to paymentCollection model
  3. If yes, triggers paymentCollectionTransaction() method
  4. Processing happens synchronously within the database transaction

Processing Flow:

Excel Upload

Media Entity Insert

afterInsert Event Fired

Read Excel using ExcelService

For Each Row in Excel:

├── Find or Create Student
│ ├── Search by Student ID + Institute
│ ├── If found: Update details
│ └── If not found: Create new

├── Create Payment Collection Items
│ └── For each Fee Type with amount > 0:
│ ├── Read amount, due date, mode
│ ├── If mode = CASH:
│ │ └── Status = "Fully Paid"
│ └── If mode = PG:
│ └── Status = "Pending"

└── Send Email Notification
├── If all items CASH (Fully Paid):
│ └── Send payment success email
└── If any items PG (Pending):
└── Send due fees email with payment link

Key Code Pattern:

import { ExcelService } from "@solidxai/core";
import { createReadStream } from 'fs';

@EventSubscriber()
export class MediaTransactionSubscriber implements EntitySubscriberInterface<Media> {
constructor(
private readonly excelService: ExcelService,
// ... other dependencies
) {}

async afterInsert(event: InsertEvent<Media>) {
const media = event.entity;

if (media.modelName === 'paymentCollection') {
await this.paymentCollectionTransaction(media, event.manager);
}
}

async paymentCollectionTransaction(media: Media, entityManager: EntityManager) {
// Get file path from media entity
const filePath = media.filePath; // Adjust based on your Media entity structure

// Create a readable stream from the file path
const fileStream = createReadStream(filePath);

// Read the entire Excel file using ExcelService
const { headers, rows } = await this.excelService.readExcelFromStreamNonStreaming(
fileStream,
{
hasHeaderRow: true,
worksheetIndex: 0
}
);

// Process each row
for (const row of rows) {
await this.processRow(row, headers, entityManager);
}
}

async processRow(
row: Record<string, any>,
headers: string[],
entityManager: EntityManager
) {
// Extract student data from row using header keys
const studentData = {
studentId: row['Student Id'],
studentName: row['Student Name'],
parentName: row['Parent/Guardian Name'],
parentEmailAddress: row['Parent/Guardian Email']?.toLowerCase(),
parentMobileNumber: row['Parent/Guardian Mobile'],
// ... other fields
};

// Process student and payment items
// ... (see next section for details)
}
}

4. Student Management Logic

Method: processRow() in Media Transaction Subscriber

Student Creation/Update Logic:

async processRow(
row: Record<string, any>,
headers: string[],
entityManager: EntityManager
) {
// Extract student data from the row object
// The ExcelService returns rows as objects with header keys
const studentData = {
studentId: row['Student Id'],
studentName: row['Student Name'],
parentName: row['Parent/Guardian Name'],
parentEmailAddress: row['Parent/Guardian Email']?.toLowerCase(),
parentMobileNumber: row['Parent/Guardian Mobile'],
institute: { id: instituteId }
};

// Find existing student
let student = await entityManager.findOne(Student, {
where: {
studentId: studentData.studentId,
institute: { id: instituteId }
}
});

if (student) {
// Update existing student
student.studentName = studentData.studentName;
student.parentName = studentData.parentName;
student.parentEmailAddress = studentData.parentEmailAddress;
student.parentMobileNumber = studentData.parentMobileNumber;
} else {
// Create new student
student = entityManager.create(Student, studentData);
}

await entityManager.save(Student, student);

// Continue processing payment items for this student
await this.createPaymentItems(row, headers, student, entityManager);
}

Important Notes:

  • Student uniqueness is determined by: Student ID + Institute
  • Updates overwrite existing data (name, parent info, contact details)
  • Student Login ID is generated separately (not from Excel)
  • Institute is always set from logged-in user's context i.e (pre-populated on the frontend depending on the institute the Institute Admin belongs to)
  • The ExcelService's readExcelFromStreamNonStreaming() returns rows as objects with column headers as keys, making data access simpler and more readable

5. Payment Collection Item Creation

For Each Fee Type in Excel:

async createPaymentItems(
row: Record<string, any>,
headers: string[],
student: Student,
entityManager: EntityManager
) {
// Get fee types for this institute
const instituteFeeTypes = await entityManager.find(FeeType, {
where: { institute: { id: instituteId } }
});

// Extract fee type columns from headers
const feeTypeColumns = headers.filter(
header => !['Student Name', 'Student Id', 'Parent/Guardian Name',
'Parent/Guardian Email', 'Parent/Guardian Mobile',
'Payment Mode'].includes(header) &&
!header.includes('Due Date')
);

// Process each fee type
for (const feeType of instituteFeeTypes) {
const amountColumnName = `${feeType.feeType} Amount`;
const dueDateColumnName = `${feeType.feeType} Due Date`;

// Access data using the column names as object keys
const amount = row[amountColumnName];
const dueDate = row[dueDateColumnName];
const paymentMode = row['Payment Mode'] || 'PG';

// Skip if no amount
if (!amount || parseFloat(amount) <= 0) continue;

// Determine status based on payment mode
let status, amountPaid, amountPending;

if (paymentMode.toString().toUpperCase() === 'CASH') {
status = 'Fully Paid';
amountPaid = parseFloat(amount);
amountPending = 0;
} else {
status = 'Pending';
amountPaid = 0;
amountPending = parseFloat(amount);
}

// Create payment collection item
const item = entityManager.create(PaymentCollectionItem, {
student: student,
feeType: feeType,
paymentCollection: paymentCollection,
institute: { id: instituteId },
amountToBePaid: parseFloat(amount),
dueDate: new Date(dueDate),
partPaymentAllowed: feeType.partPaymentAllowed,
status: status,
amountPaid: amountPaid,
amountPending: amountPending,
totalAmountToBePaid: parseFloat(amount),
mode: paymentMode.toString().toUpperCase()
});

await entityManager.save(PaymentCollectionItem, item);
}
}

Benefits of Using ExcelService:

  • Clean object-based access to cell values using column headers as keys
  • Automatic handling of different Excel cell types (numbers, dates, strings)
  • Built-in support for rich text, formulas, and hyperlinks
  • No need to manage cell indices or column mappings manually
  • Consistent data normalization across the application

6. Email Notification Logic

Method: sendEmailNotification() in Media Transaction Subscriber

Decision Logic:

// Fetch all items for this student + payment collection
const items = await entityManager.find(PaymentCollectionItem, {
where: {
student: { id: student.id },
paymentCollection: { id: paymentCollection.id }
},
relations: ['feeType', 'institute']
});

// Check if all items are fully paid (CASH mode)
const allFullyPaid = items.every(item => item.status === 'Fully Paid');

if (allFullyPaid) {
// Send payment success email
await mailFactory.sendEmailUsingTemplate(
student.parentEmailAddress, //to
'fees-portal-payment-success', //template name
{ // templateParams
student: student,
paymentDetails: {
totalAmount: items.reduce((sum, item) => sum + item.amountPaid, 0),
feeTypes: items.map(item => item.feeType.feeType).join(', '),
status: 'Fully Paid',
createdDate: new Date()
},
instituteLogo: institute.logo,
},
true //shouldQueueEmails
);
} else {
// Send due fees email with payment link
const redirectUrl = `https://${institute.hostedPagePrefix}-${EDU_BASE_DOMAIN}/?id=${student.studentLoginId}`;

await mailFactory.sendEmailUsingTemplate(
student.parentEmailAddress, //to
'new-payment-or-payment-reminder', //template name
{ // templateParams
student: student,
dueDetails: {
totalAmount: items.reduce((sum, item) => sum + item.amountPending, 0),
feeTypes: items.map(item => item.feeType.feeType).join(', '),
status: 'Pending',
redirectUrl: redirectUrl,
createdDate: new Date()
},
instituteLogo: institute.logo,
},
true //shouldQueueEmails
);
}

Email Templates:

  1. Payment Success Email (fees-portal-payment-success.handlebars.html)

    • Triggered when: All items have status "Fully Paid" (CASH mode)
    • Shows: Confirmation of payment received, total amount, fee types
    • No action needed from parent
  2. Due Fees Email (fees-portal-new-payment-or-payment-reminder.handlebars.html)

    • Triggered when: Any item has status "Pending" (PG mode)
    • Shows: Amount due, fee types, due dates, payment link
    • Call to action: "Pay Now" button linking to student portal

Template Location: solid-api/module-metadata/fees-portal/email-templates/

7. Computed Field for Amount Calculations

When payments are made through the payment gateway, amounts need to be recalculated.

Implementation Location: Payment Collection Item Amount Provider

  • File: solid-api/src/fees-portal/computed-providers/payment-collection-item-amount-provider.ts
  • Trigger: After Payment Collection Item Detail save

What It Does:

// Triggered when payment detail is saved
async computeFieldValue(entity: PaymentCollectionItemDetail) {
const item = entity.paymentCollectionItem;

// Sum all successful payment details
const totalPaid = await this.sumSuccessfulPayments(item.id);

// Calculate pending amount
const totalAmount = item.amountToBePaid + (item.lateAmountToBePaid || 0);
const pending = totalAmount - totalPaid;

// Determine status
let status;
if (pending <= 0) {
status = 'Fully Paid';
} else if (totalPaid > 0) {
status = 'Partially Paid';
} else {
status = 'Pending';
}

// Update item
await this.update(item.id, {
amountPaid: totalPaid,
amountPending: pending,
totalAmountToBePaid: totalAmount,
status: status
});
}

8. Scheduled Jobs

The payment collection feature uses two scheduled jobs to automate late fee calculation and email reminders.

Reference Documentation

📋 For detailed information about scheduled jobs configuration, properties, and best practices, see Scheduled Jobs

Job 1: Late Fee Calculator

  • File: solid-api/src/fees-portal/scheduled-jobs/late-fee-payment-calculator-scheduled-job.service.ts
  • Class Name: LateFeePaymentCalculatorScheduledJob
  • Frequency: Hourly (runs every hour, all days of the week)
  • Purpose: Calculate and apply late fees for overdue payments

Configuration in metadata JSON:

{
"scheduleName": "Late Fee Calculation",
"isActive": true,
"frequency": "Hourly",
"job": "LateFeePaymentCalculatorScheduledJob",
"moduleUserKey": "fees-portal"
}

What It Does:

// Find overdue items
const overdueItems = await this.find({
where: {
dueDate: LessThan(new Date()),
status: Not(In(['Cancelled', 'Fully Paid']))
},
relations: ['feeType']
});

// Calculate late fees
for (const item of overdueItems) {
const overdueDays = Math.floor((today - item.dueDate) / (1000 * 60 * 60 * 24));
const pendingAmount = parseFloat(item.amountPending);

let lateFee = 0;
if (item.feeType.latePaymentFeesType === 'Percent') {
lateFee = (pendingAmount * item.feeType.latePaymentFees) / 100;
} else if (item.feeType.latePaymentFeesType === 'Absolute') {
lateFee = item.feeType.latePaymentFees;
}

await this.update(item.id, {
isOverdue: true,
overdueByDays: overdueDays,
lateAmountToBePaid: lateFee,
totalAmountToBePaid: pendingAmount + lateFee
});
}

Job 2: Fees Due Email Reminder

  • File: solid-api/src/fees-portal/scheduled-jobs/send-email-schedule-jobs.service.ts
  • Class Name: SendEmailScheduleJobs
  • Frequency: Daily (runs once per day, all days of the week)
  • Purpose: Send payment reminders to parents with pending fees

Configuration in metadata JSON:

{
"scheduleName": "Fees Due Email",
"isActive": true,
"frequency": "Daily",
"job": "SendEmailScheduleJobs",
"moduleUserKey": "fees-portal"
}

What It Does:

// Find pending items
const pendingItems = await this.find({
where: {
status: In(['Pending', 'Partially Paid'])
},
relations: ['student', 'feeType', 'institute']
});

// Group by student
const groupedByStudent = this.groupBy(pendingItems, 'student.id');

// Send reminders
for (const [studentId, items] of Object.entries(groupedByStudent)) {
const student = items[0].student;
const institute = items[0].institute;

await mailFactory.sendEmailUsingTemplate(
student.parentEmailAddress,
'new-payment-or-payment-reminder',
{
student: student,
dueDetails: {
totalAmount: items.reduce((sum, item) => sum + parseFloat(item.amountPending), 0),
feeTypes: items.map(item => item.feeType.feeType).join(', '),
redirectUrl: `https://${institute.hostedPagePrefix}-${EDU_BASE_DOMAIN}/?id=${student.studentLoginId}`
},
instituteLogo: institute.logo,
},
true
);
}
Implementation Order

Follow this sequence when implementing custom business logic:

  1. Create validation service method (feeTypeValidation)
  2. Implement template generation service
  3. Create event subscriber for Excel processing
  4. Implement student creation/update logic
  5. Implement payment item creation logic
  6. Implement email notification logic
  7. Create computed field provider for amount calculations
  8. Set up scheduled jobs for late fees and reminders
  9. Test end-to-end flow with sample Excel file
Excel Service Integration

All Excel file operations in this feature use the centralized ExcelService for consistency:

Key Methods Used:

  • readExcelFromStreamNonStreaming(): For validation and processing Excel files

    • Returns { headers: string[], rows: Record<string, any>[] }
    • Handles complex cell types (rich text, formulas, dates)
    • Provides clean object-based access using column headers as keys
  • createExcelStream(): For generating templates

    • Accepts custom headers array
    • Creates downloadable Excel files with proper formatting

Benefits:

  • Consistent data normalization across the application
  • Automatic handling of Excel-specific data types
  • Built-in support for streaming large files
  • Clean, maintainable code without low-level ExcelJS complexity
  • Reusable service for all import/export operations

Alternative Approach: For very large files that may cause memory issues, consider using readExcelInPagesFromStream() which returns an async generator for processing rows in chunks. However, for typical payment collection files (hundreds to thousands of rows), readExcelFromStreamNonStreaming() provides better performance and simpler code.

Customizing the UI

After generating the code using SolidX, default list and form views are automatically created for each model. However, these default views need customization to provide a streamlined payment collection workflow. This section explains how to customize these views using layout JSON configuration and UI extensions.

To learn how to apply these customizations, see Applying View Customizations.

Payment Collection Form View Customizations

The customized Payment Collection Form View with template download functionality.

1. Simplified Field Layout

The Payment Collection form displays only essential fields in a single-column layout:

FieldPurposeVisibility
NameIdentify this collection batchAlways visible
DescriptionAdditional context (optional)Always visible
Due DateDefault due date for all itemsAlways visible
InstituteWhich institute (auto-filled for Institute Admin)Hidden for new records, visible when editing
Payment FileExcel uploadDisabled when editing existing records

Layout JSON Snippet:

{
"type": "column",
"attrs": {
"name": "group-1",
"label": "Payment Collection Details",
"className": "col-12"
},
"children": [
{
"type": "field",
"attrs": {
"name": "name"
}
},
{
"type": "field",
"attrs": {
"name": "description"
}
},
{
"type": "field",
"attrs": {
"name": "dueDate"
}
},
{
"type": "field",
"attrs": {
"name": "institute"
}
},
{
"type": "field",
"attrs": {
"name": "paymentFile"
}
}
]
}

Benefits:

  • Simple, focused interface for primary task
  • No overwhelming options or tabs
  • Clear workflow: Select Institute → Select Due Date → Upload

2. Form Load Handler for Conditional Field Behavior and Pre-filling

Extension: paymentCollectionOnFormLoadHandler.ts

This unified handler controls field behavior and pre-fills data based on context:

export function paymentCollectionOnFormLoadHandler(formContext) {
const { formData, userData, userRole, isEditMode } = formContext;

// Disable fields when editing existing record
if (isEditMode) {
formContext.setFieldProperty('name', 'disabled', true);
formContext.setFieldProperty('institute', 'disabled', true);
formContext.setFieldProperty('paymentFile', 'disabled', true);
}

// Auto-fill institute for Institute Admin users on new records
if (!isEditMode && userRole === 'Institute Admin' && userData.institute) {
formContext.setFieldValue('institute', userData.institute.id);
}
}

Rationale:

  • Disable name when editing: Payment collection name identifies the batch; changing it after processing would cause confusion
  • Disable institute when editing: Institute cannot change after items are created
  • Disable payment file when editing: Excel is processed on upload; re-uploading would create duplicate items
  • Auto-fill institute: Institute Admins only manage their own institute
  • Unified handler: Combines layout and data loading logic in a single handler for better maintainability

Benefits:

  • Single handler for all form initialization logic
  • Reduces data entry for Institute Admins
  • Maintains data integrity
  • Follows current best practices

4. Excel Template Download Button

Add a button directly in the form header using the formButtons configuration. This button will trigger a custom action to download the Excel template.

Step 1: Configure Form Button in Layout JSON

{
"name": "paymentCollection-form-view",
"displayName": "Initiate Payments",
"type": "form",
"context": "{}",
"moduleUserKey": "fees-portal",
"modelUserKey": "paymentCollection",
"layout": {
"type": "form",
"attrs": {
"name": "form-1",
"label": "Initiate Payments",
"className": "grid",
"showCogWheelFormButton": false,
"formButtons": [
{
"attrs": {
"className": "p-button p-component p-button-sm",
"icon": "pi pi-download",
"label": "Download Sample Excel",
"action": "GenerateTemplateFormat",
"customComponentIsSystem": true,
"actionInContextMenu": false,
"openInPopup": true
}
}
]
},
"onFormLoad": "paymentCollectionOnFormLoadHandler",
"children": [
// ... rest of form layout
]
}
}

This configuration adds a "Download Sample Excel" button in the form header that:

  • Displays the download icon (pi pi-download)
  • Triggers the GenerateTemplateFormat action when clicked
  • Opens in a popup for better user experience
  • Appears alongside other form action buttons

Step 2: Implement the Custom Action Component

Create the action handler that will be triggered when the button is clicked. SolidX automatically maps the action name from the form button configuration to the corresponding component file.

File: generate-template-format.tsx

import React from 'react';
import { Button } from 'primereact/button';

export function GenerateTemplateFormat({ instituteId }) {
const handleDownload = async () => {
const url = `/api/generate-fee-reports?id=${instituteId}`;
window.open(url, '_blank');
};

return (
<div className="template-download-section">
<h3>Step 1: Download Excel Template</h3>
<p>Download the template with your institute's configured fee types</p>
<Button
label="Download Template"
icon="pi pi-download"
onClick={handleDownload}
className="p-button-success"
/>
<hr />
<h3>Step 2: Upload Completed Excel</h3>
<p>Fill the template with student and payment details, then upload below</p>
</div>
);
}

Benefits:

  • Seamless integration with form header actions
  • Guides users through workflow (Download → Fill → Upload)
  • Template matches institute's fee types dynamically
  • Reduces formatting errors
  • Improves user experience with clear step-by-step instructions

Complete Payment Collection Form Layout JSON

Below is the complete form layout JSON for the Payment Collection model, including the formButtons configuration:

Click to expand the complete JSON layout
{
"type": "form",
"attrs": {
"name": "form-1",
"label": "Payment Collection",
"className": "grid",
"showCogWheelFormButton": false,
"formButtons": [
{
"attrs": {
"className": "p-button p-component p-button-sm",
"icon": "pi pi-download",
"label": "Download Sample Excel",
"action": "GenerateTemplateFormat",
"customComponentIsSystem": true,
"actionInContextMenu": false,
"openInPopup": true
}
}
]
},
"onFormLoad": "paymentCollectionOnFormLoadHandler",
"children": [
{
"type": "sheet",
"attrs": {
"name": "sheet-1"
},
"children": [
{
"type": "row",
"attrs": {
"name": "row-1"
},
"children": [
{
"type": "column",
"attrs": {
"name": "group-1",
"label": "Payment Collection Details",
"className": "col-12"
},
"children": [
{
"type": "field",
"attrs": {
"name": "name"
}
},
{
"type": "field",
"attrs": {
"name": "description"
}
},
{
"type": "field",
"attrs": {
"name": "dueDate"
}
},
{
"type": "field",
"attrs": {
"name": "institute",
"visible": true
}
},
{
"type": "field",
"attrs": {
"name": "paymentFile"
}
}
]
}
]
}
]
}
]
}

Payment Collection List View Customizations

1. Key Columns Display

The list view shows essential information for managing collections:

ColumnPurposeSortableFilterable
NameCollection identifierYesYes
Due DateWhen payments are dueYesYes
InstituteWhich institute (Super Admin view)YesYes
Created DateWhen collection was createdYesYes
Created ByWho created the collectionNoYes

2. Role-Based Action Permissions

"configureViewActions": {
"create": { "roles": ["Admin", "Institute Admin"] },
"edit": { "roles": ["Admin", "Institute Admin"] },
"delete": { "roles": ["Admin"] },
"import": { "roles": [] },
"export": { "roles": ["Admin", "Institute Admin"] },
"customizeLayout": { "roles": ["Admin", "Institute Admin"] }
}

Benefits:

  • Institute Admins can create and edit collections
  • Only Super Admin can delete collections (data integrity)
  • Import disabled (use Excel upload instead)
  • Export enabled for reporting

Payment Collection Item List View Columns

Shows all critical information for tracking payments:

ColumnPurposeWidget/Format
StudentWho owes the feeLink to student record
Fee TypeWhat fee is owedText
Amount To Be PaidBase amountCurrency
Amount PaidPaid so farCurrency (green if > 0)
Amount PendingStill owedCurrency (red if > 0)
Late AmountLate fee penaltyCurrency (highlighted if > 0)
Total AmountBase + Late feesCurrency (bold)
StatusCurrent statusBadge (colored by status)
Due DatePayment deadlineDate (red if overdue)
Is OverduePast due?Boolean icon
Overdue By DaysDays past dueNumber (red if > 0)
Payment ModeCASH or PGBadge
Payment CollectionWhich batchLink to collection

Student List View Customizations

1. Key Columns

ColumnPurpose
Student NameFull name
Student IDUnique identifier
Parent/Guardian NamePrimary contact
Parent/Guardian EmailEmail for notifications
Parent/Guardian MobilePhone contact
InstituteWhich institute

2. Role-Based Column Visibility

Column visibility is controlled by adding a roles key to individual field configurations in the list view layout JSON.

Example: Hiding Institute Column from Institute Admins

View: student-list-view

List View Layout Configuration:

{
"name": "student-list-view",
"displayName": "Students",
"type": "list",
"moduleUserKey": "fees-portal",
"modelUserKey": "student",
"layout": {
"type": "list",
"attrs": {
// ... list configuration
},
"children": [
{
"type": "field",
"attrs": {
"name": "studentName"
}
},
{
"type": "field",
"attrs": {
"name": "institute",
"roles": ["Admin"] // ← Add roles key here
}
}
// ... other fields
]
}
}

Key Points:

  • Add the roles property inside the field's attrs object
  • The roles array specifies which user roles can see this column
  • Only users with "Admin" role will see the institute column i.e Super Admin, since Super Admin is implicitly given "Admin" role in the system
  • Institute Admins (without "Admin" role) will not see this column
  • If roles is not specified, the column is visible to all users

Benefits:

  • Super Admin sees institute column (multi-institute view)
  • Institute Admins don't see institute column (single institute context)
  • Cleaner, role-appropriate interface for each user type

General Design Principles

The customizations follow these principles:

  1. Task-Oriented Workflow: Form guides users through clear steps (Download → Upload)
  2. Progressive Disclosure: Only show fields relevant to current context
  3. Role-Appropriate Views: Show only what each role needs to see
  4. Error Prevention: Disable fields that shouldn't be changed after processing
  5. Clarity: Clear labels and helper text guide users

These customizations transform the auto-generated UI into an intuitive payment collection workflow that minimizes errors and maximizes efficiency.

Applying Your Customizations

Now that you have the customized layout JSONs ready, follow the steps in Applying View Customizations to apply them via the Layout Builder.

Excel File Format and Requirements

This section provides detailed specifications for the Excel file format required for payment collection upload.

File Format Specifications

SpecificationRequirement
File Type.xlsx (Excel 2007 or later)
Maximum File Size5 MB
Sheet NameAny (first sheet is processed)
Header RowRow 1 must contain column headers
Data RowsStart from Row 2 onwards
Maximum RowsNo hard limit (practical limit based on processing time)

Required Columns

These columns must be present in your Excel file:

Column NameData TypeRequiredFormat/ValidationExample
Student NameTextYesAny text"Rahul Sharma"
Student IdTextYesMust be unique per institute"STU2024001"
Parent/Guardian NameTextYesAny text"Mr. Rajesh Sharma"
Parent/Guardian EmailEmailYesValid email format, lowercase"rajesh.sharma@example.com"
Parent/Guardian MobileTextYes10-digit number"9123456789"

Dynamic Fee Type Columns

For each fee type configured in your institute, add TWO columns:

Pattern: {Fee Type Name} Amount and {Fee Type Name} Due Date

Example: If your institute has "Tuition Fees" and "Bus Fees" configured:

Column NameData TypeRequiredFormatExample
Tuition Fees AmountNumberNo*Decimal number, no commas10000 or 10000.50
Tuition Fees Due DateDateNo*yyyy-mm-dd or Excel date"2024-04-30" or Excel date
Bus Fees AmountNumberNo*Decimal number, no commas3000
Bus Fees Due DateDateNo*yyyy-mm-dd or Excel date"2024-04-30"

Important Notes:

  • Fee type names must exactly match those configured in your institute's Fee Type master data
  • Column names are case-sensitive
  • If amount is 0 or empty for a fee type, no payment item will be created for that fee type
  • Due date is required if amount > 0

Payment Mode Column

Column NameData TypeRequiredFormatExample
Payment ModeTextNo (defaults to "PG")"CASH" or "PG" (case-insensitive)"PG"

Values:

  • PG (Payment Gateway): Student will receive email with payment link; status will be "Pending"
  • CASH: Payment already collected offline; status will be "Fully Paid"; student receives confirmation

Sample Excel Structure

Here's how a complete Excel file should look:

Student NameStudent IdParent/Guardian NameParent/Guardian EmailParent/Guardian MobileTuition Fees AmountTuition Fees Due DateBus Fees AmountBus Fees Due DateLab Fees AmountLab Fees Due DatePayment Mode
Rahul SharmaSTU001Mr. Rajesh Sharmarajesh.sharma@example.com9123456789100002024-04-3030002024-04-300PG
Priya PatelSTU002Mrs. Meena Patelmeena.patel@example.com9876543210100002024-04-3015002024-05-15PG
Amit KumarSTU003Mr. Suresh Kumarsuresh.kumar@example.com9988776655100002024-04-3030002024-04-3015002024-05-15CASH

Analysis of Sample Data:

Row 2 (Rahul Sharma):

  • Will create 2 payment items: Tuition Fees (₹10,000) and Bus Fees (₹3,000)
  • Lab Fees has amount 0, so no item created
  • Both items will have status "Pending" (PG mode)
  • Student will receive email with payment link

Row 3 (Priya Patel):

  • Will create 2 payment items: Tuition Fees (₹10,000) and Lab Fees (₹1,500)
  • Bus Fees has no amount, so no item created
  • Lab Fees has different due date (2024-05-15)
  • Both items status "Pending"
  • Student will receive email with payment link

Row 4 (Amit Kumar):

  • Will create 3 payment items: All three fee types
  • All items will have status "Fully Paid" (CASH mode)
  • Student will receive payment confirmation email
  • No payment link needed
  • Student receives payment confirmation email

Common Validation Errors and Solutions

Error MessageCauseSolution
"Fee type 'XYZ' is not configured"Excel contains fee type not in institute's master dataAdd fee type to institute configuration first, or remove column from Excel
"Due date cannot be in the past"Due date is before todayChange due date to today or future date
"Invalid payment mode"Payment Mode is not "CASH" or "PG"Use only "CASH" or "PG" (case doesn't matter)
"Parent/Guardian Email is required"Email column is emptyFill in email address for all students
"Invalid email format"Email is malformedCheck email format (must contain @)
"Student Id is required"Student Id column is emptyProvide unique student ID for each row
"Amount must be greater than 0"Negative or invalid amountEnter positive numbers only

Best Practices

Data Entry:

  1. Use the generated template: Download template from the form to ensure correct structure
  2. Check fee type names: Copy fee type names exactly as configured in your institute
  3. Consistent date format: Use yyyy-mm-dd format for dates (e.g., 2024-04-30)
  4. No special formatting: Don't use currency symbols (₹, $), commas, or colors
  5. One row per student: Each student should appear only once in the file
  6. Complete all required fields: Don't leave required columns empty

Before Upload:

  1. Remove extra rows/columns: Delete any template instructions or extra headers
  2. Verify student IDs: Ensure student IDs are unique
  3. Check due dates: Confirm all dates are today or in the future
  4. Validate amounts: All amounts should be positive numbers
  5. Review payment modes: Use only "CASH" or "PG"
  6. Test with small batch: Upload 5-10 students first to verify format

After Upload:

  1. Check processing status: Verify no error messages appear
  2. Review created items: Check Payment Collection Items list
  3. Verify student emails: Confirm parents received notification emails
  4. Spot check amounts: Verify amounts match your Excel

Troubleshooting

Upload fails immediately:

  • Check file size (must be < 5 MB)
  • Verify file format (.xlsx only)
  • Ensure first row contains headers

Upload succeeds but no items created:

  • Check if amounts are > 0
  • Verify fee type names match exactly
  • Review validation error messages

Some students missing:

  • Check for empty required fields
  • Verify email format
  • Check Student ID for duplicates in Excel

Wrong number of items created:

  • Count non-zero amounts in Excel
  • Each non-zero amount creates one item
  • Zero or empty amounts are skipped
Testing Recommendation

Before uploading payment collections for all students:

  1. Create a test Excel with 2-3 students
  2. Use different scenarios: PG mode, CASH mode, multiple fee types
  3. Upload and verify results
  4. Check emails were sent correctly
  5. Once verified, proceed with full batch

Payment Collection Workflow

This section provides a comprehensive step-by-step guide for Institute Admins to create and manage payment collections.

Prerequisites

Before initiating a payment collection, ensure:

  • Institute is activated (status = "Active")
  • Fee Types are configured for your institute
  • Late payment rules are set for each fee type (if applicable)
  • Payment gateway credentials are configured
  • Email templates are configured

Phase 1: Prepare Payment Collection Data

Step 1: Gather Student Information

Collect the following information for all students:

  • Student Name
  • Student ID (roll number, admission number)
  • Parent/Guardian Name
  • Parent/Guardian Email (for notifications)
  • Parent/Guardian Mobile Number

Step 2: Determine Fee Amounts and Due Dates

For each student and each fee type:

  • Calculate fee amount (based on your fee structure)
  • Determine due date (can be same for all or different by fee type)
  • Decide payment mode:
    • PG: Student will pay online through portal
    • CASH: Payment already collected offline

Step 3: Prepare Collection Name and Description

Choose a clear, descriptive name for this batch:

  • Good examples:
    • "Q1 2024 Tuition and Bus Fees"
    • "Annual Sports Fees 2024"
    • "January 2024 Monthly Fees"
  • Add description (optional):
    • "First quarter fees including tuition, lab, and bus charges"
    • "Annual sports fees for academic year 2024-25"

Phase 2: Download and Fill Excel Template

Step 4: Login to Fees Portal

  • Navigate to the Fees Portal admin interface
  • Login using Institute Admin credentials
  • You will see only your institute's data

Step 5: Navigate to Payment Collections

  • Click on "Fees Portal" module in the sidebar
  • Select "Payment Collections" from the menu
  • Click on the "Add" button

Step 6: Download Excel Template

  • In the form, locate the "Download Sample Excel" section at the top
  • Click "Download Sample Excel" button
  • Save the Excel file to your computer

The template will contain:

  • Fixed columns: Student Name, Student Id, Parent/Guardian details
  • Dynamic columns: For each fee type configured in your institute
  • Example: If you have "Tuition Fees", "Bus Fees", "Lab Fees" configured:
    • Tuition Fees Amount
    • Tuition Fees Due Date
    • Bus Fees Amount
    • Bus Fees Due Date
    • Lab Fees Amount
    • Lab Fees Due Date
  • Last column: Payment Mode

Step 7: Fill the Excel Template

Open the downloaded template and fill in the data:

For each student (one row per student):

  1. Enter student details:

    • Student Name: Full name
    • Student Id: Unique identifier (must be consistent across collections)
    • Parent/Guardian Name: Primary contact person
    • Parent/Guardian Email: Email where notifications will be sent (lowercase)
    • Parent/Guardian Mobile: 10-digit mobile number
  2. Enter fee amounts and due dates:

    • For each fee type, enter the amount in the Fee Type Amount column
    • Enter the due date in the Fee Type Due Date column (format: yyyy-mm-dd)
    • If a student doesn't need to pay a particular fee type, leave the amount as 0 or empty
    • Example:
      Tuition Fees Amount: 10000
      Tuition Fees Due Date: 2024-04-30
      Bus Fees Amount: 3000
      Bus Fees Due Date: 2024-04-30
      Lab Fees Amount: 0 (or leave empty - no item will be created)
  3. Enter payment mode:

    • Use "PG" if student will pay online through payment gateway
    • Use "CASH" if payment has already been collected offline
    • Leave empty to default to "PG"

Important Notes:

  • Don't change column names
  • Don't add or remove columns
  • Don't use currency symbols (₹, $) or commas in amounts
  • Use numbers only for amounts (e.g., 10000.50)
  • Use yyyy-mm-dd format for dates (e.g., 2024-04-30)
  • Ensure all due dates are today or in the future
  • Each student should appear only once in the file

Step 8: Save and Validate Excel

Before uploading:

  • Double-check all required fields are filled
  • Verify student IDs are unique
  • Confirm due dates are not in the past
  • Check email addresses are valid
  • Verify amounts are positive numbers
  • Confirm payment modes are "PG" or "CASH"

Phase 3: Upload Payment Collection

Step 9: Fill Form Details

Return to the Payment Collection form in the portal:

FieldWhat to EnterExample
NameDescriptive name for this collection"Q1 2024 Tuition and Bus Fees"
DescriptionAdditional context (optional)"First quarter fees for all students"
Due DateDefault due date (can be overridden in Excel)2024-04-30
InstituteAuto-filled (Institute Admin)Your institute

Step 10: Upload Excel File

  • Click "Choose File" in the "Payment File" field
  • Select your completed Excel file
  • Verify file name appears

Step 11: Submit Form

  • Click "Save" button
  • System will validate the Excel file
  • If validation fails, error messages will appear:
    • Read error messages carefully
    • Fix issues in Excel
    • Upload again
  • If validation succeeds, form will be saved

Step 12: Wait for Processing

After successful upload:

  • System processes Excel file in the background
  • For each row in Excel:
    • Student record is created or updated
    • Payment Collection Items are created for each fee type
    • Email notifications are queued
  • Processing time depends on number of students (typically 1-2 minutes for 100 students)

Phase 4: Verify Results

Step 13: Check Payment Collection Items

  • Navigate to "Payment Collection Items" menu
  • Filter by your payment collection name
  • Verify correct number of items created:
    • Count = (Number of students) × (Number of fee types with amount > 0 per student)
    • Example: 50 students, each paying 3 fee types = 150 items

Step 14: Verify Item Details

For a few sample students, check:

  • Student name and ID are correct
  • Fee type is correct
  • Amount to be paid matches Excel
  • Due date is correct
  • Payment mode (CASH or PG) is correct
  • Status is correct:
    • "Fully Paid" for CASH mode
    • "Pending" for PG mode

Step 15: Check Email Notifications

Verify emails were sent:

For PG mode items:

  • Parents should receive "New Payment Request" email
  • Email contains:
    • Student name and details
    • Total amount due
    • List of fee types
    • Payment link to student portal
    • Due date

For CASH mode items:

  • Parents should receive "Payment Confirmation" email
  • Email contains:
    • Student name and details
    • Total amount paid
    • List of fee types
    • Payment confirmation message

Phase 5: Monitor and Manage

Step 16: Track Payment Status

Regularly check Payment Collection Items list:

  • Filter by "Pending" or "Partially Paid" status
  • Monitor overdue items (Is Overdue = true)
  • Check late fees being applied (Late Amount To Be Paid > 0)

Step 17: Handle Common Scenarios

Scenario 1: Student Made Partial Payment

  • Status will automatically change to "Partially Paid"
  • Amount Paid and Amount Pending will update
  • Student can see remaining balance in portal

Scenario 2: Payment is Overdue

  • Late fee calculator job runs daily
  • Is Overdue flag set to true
  • Late Amount To Be Paid calculated based on Fee Type configuration
  • Total Amount To Be Paid = Base Amount + Late Fee
  • Student sees updated amount in portal

Phase 6: Ongoing Management

Step 18: Send Payment Reminders

The system automatically sends reminder emails on a schedule (typically weekly):

  • Targets students with "Pending" or "Partially Paid" status
  • Email contains:
    • Updated amount (including late fees if overdue)
    • Days overdue (if applicable)
    • Payment link
TODO

You can also manually trigger reminders if needed.

Step 19: Generate Reports

Use the export feature to download payment collection data:

  • Navigate to Payment Collection Items list
  • Apply filters (e.g., "Pending", "Overdue")
  • Click "Export" button
  • Download Excel file with current data
  • Use for analysis, reporting, or record-keeping

Step 20: Reconcile Payments

Periodically reconcile payment records:

  • Check Payment Collection Item Details for transaction history
  • Verify amounts match payment gateway records
  • Investigate any discrepancies
  • Update records if needed

Troubleshooting Reference

IssuePossible CauseSolution
Excel upload failsInvalid file format, size too large, or structural issuesCheck file type (.xlsx), size < 5MB, verify headers
No items createdAll amounts are 0 or emptyEnsure at least one fee type has amount > 0
Wrong number of itemsZero amounts in ExcelOnly non-zero amounts create items
Validation errorFee type not configured, past due dates, invalid emailsRead error message, fix Excel, re-upload
No email sentInvalid email address, email service issueVerify email in Student record, check email logs
Payment link doesn't workStudent Login ID not generatedCheck Student record for studentLoginId field
Status not updatingPayment gateway webhook not configuredContact technical team to verify webhook setup
Late fees not calculatedScheduled job not runningContact technical team to verify job configuration

Success Criteria

You've successfully initiated a payment collection when:

  • Excel template downloads with your fee types
  • Excel validates without errors
  • Payment Collection is created
  • Correct number of Payment Collection Items created
  • Students appear in system with correct details
  • Email notifications sent to all parents
  • Payment links work for PG mode students
  • CASH mode students show "Fully Paid" status
  • Payment status updates when students pay online (We will cover this in the next section)

Congratulations! You've successfully initiated a payment collection. Students can now view and pay their fees through the student portal, and you can monitor payment status in real-time.