WEB-277: Collection sheet and Productive collection sheet part I

This commit is contained in:
Jose Alberto Hernandez
2025-09-02 21:36:31 -05:00
parent f1adfaf9e1
commit dbf551fcd1
25 changed files with 412 additions and 21 deletions
@@ -174,7 +174,7 @@ export class AttachCenterMeetingComponent implements OnInit {
locale
};
this.centersService.createCenterMeeting(this.centerId, data).subscribe((response: any) => {
this.router.navigate(['../../'], { relativeTo: this.route });
this.router.navigate(['../../general'], { relativeTo: this.route });
});
}
}
+12
View File
@@ -282,4 +282,16 @@ export class CentersService {
executeEditCenter(centerId: string, data: any): Observable<any> {
return this.http.put(`/groups/${centerId}`, data);
}
getAllMeetingFallCenters(
officeId: number,
staffId: number,
meetingDate: string,
dateFormat: string,
locale: string
): Observable<any> {
return this.http.get(
`/centers?dateFormat=${dateFormat}&locale=${locale}&meetingDate=${meetingDate}&officeId=${officeId}&staffId=${staffId}`
);
}
}
@@ -24,7 +24,7 @@ export class CenterActionsResolver {
* @returns {Observable<any>}
*/
resolve(route: ActivatedRouteSnapshot): Observable<any> {
const actionName = route.paramMap.get('name');
const actionName = route.paramMap.get('action');
const centerId = route.paramMap.get('centerId') || route.parent.parent.paramMap.get('centerId');
switch (actionName) {
case 'Assign Staff':
@@ -0,0 +1,71 @@
<mat-card class="container">
<form [formGroup]="collectionSheetForm" (ngSubmit)="previewCollectionSheet()">
<div class="layout-row-wrap gap-2percent responsive-column align-start-center">
<mat-form-field class="flex-98">
<mat-label>{{ 'labels.inputs.Branch Office' | translate }}</mat-label>
<mat-select formControlName="officeId" required>
<mat-option *ngFor="let office of officesData" [value]="office.id">
{{ office.name }}
</mat-option>
</mat-select>
<mat-error *ngIf="collectionSheetForm.controls.officeId.hasError('required')">
{{ 'labels.inputs.Branch Office' | translate }} {{ 'labels.commons.is' | translate }}
<strong>{{ 'labels.commons.required' | translate }}</strong>
</mat-error>
</mat-form-field>
<mat-form-field class="flex-98" (click)="meetingDatePicker.open()" *ngIf="officeId !== null">
<mat-label>{{ 'labels.inputs.Meeting Date' | translate }}</mat-label>
<input
matInput
[min]="minDate"
[max]="maxDate"
[matDatepicker]="meetingDatePicker"
required
formControlName="meetingDate"
/>
<mat-datepicker-toggle matSuffix [for]="meetingDatePicker"></mat-datepicker-toggle>
<mat-datepicker #meetingDatePicker></mat-datepicker>
<mat-error *ngIf="collectionSheetForm.controls.meetingDate.hasError('required')">
{{ 'labels.inputs.Meeting Date' | translate }} {{ 'labels.commons.is' | translate }}
<strong>{{ 'labels.commons.required' | translate }}</strong>
</mat-error>
</mat-form-field>
<mat-form-field class="flex-98" *ngIf="officeId !== null">
<mat-label>{{ 'labels.inputs.Staff' | translate }}</mat-label>
<mat-select formControlName="staffId">
<mat-option *ngFor="let loanOfficer of loanOfficerData" [value]="loanOfficer.id">
{{ loanOfficer.displayName }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="flex-98" *ngIf="officeId !== null">
<mat-label>{{ 'labels.inputs.Group' | translate }}</mat-label>
<mat-select formControlName="groupId">
<mat-option *ngFor="let group of groupsData" [value]="group.id">
{{ group.name }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="flex-98" *ngIf="officeId !== null">
<mat-label>{{ 'labels.inputs.Center' | translate }}</mat-label>
<mat-select formControlName="centerId">
<mat-option *ngFor="let center of centersData" [value]="center.id">
{{ center.name }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<mat-card-actions class="layout-row align-center gap-5px responsive-column">
<button type="button" mat-raised-button [routerLink]="['../']">{{ 'labels.buttons.Cancel' | translate }}</button>
<button mat-raised-button color="primary" [disabled]="!collectionSheetForm.valid">
<fa-icon icon="download" class="m-r-10"></fa-icon>
{{ 'labels.buttons.Productive Collection Sheet' | translate }}
</button>
</mat-card-actions>
</form>
</mat-card>
@@ -0,0 +1,15 @@
.container {
max-width: 37em;
.margin-v {
margin: 1em 0;
}
.margin-b {
margin: 0 0 1em;
}
.margin-t {
margin-top: 1em;
}
}
@@ -0,0 +1,166 @@
import { Component, OnInit } from '@angular/core';
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { CollectionsService } from '../collections.service';
import { ActivatedRoute, Router } from '@angular/router';
import { SettingsService } from 'app/settings/settings.service';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import {
MatCell,
MatCellDef,
MatColumnDef,
MatHeaderCell,
MatHeaderCellDef,
MatHeaderRow,
MatHeaderRowDef,
MatRow,
MatRowDef,
MatTable
} from '@angular/material/table';
import { MatSort, MatSortHeader } from '@angular/material/sort';
import { MatPaginator } from '@angular/material/paginator';
import { OrganizationService } from 'app/organization/organization.service';
import { CentersService } from 'app/centers/centers.service';
import { GroupsService } from 'app/groups/groups.service';
import { Dates } from 'app/core/utils/dates';
import { CollectionSheetData, JLGGroupData, MeetingFallCenter } from '../models/collection-sheet-data.model';
@Component({
selector: 'mifosx-collection-sheet',
templateUrl: './collection-sheet.component.html',
styleUrl: './collection-sheet.component.scss',
imports: [
...STANDALONE_SHARED_IMPORTS,
FaIconComponent,
MatTable,
MatSort,
MatColumnDef,
MatHeaderCellDef,
MatHeaderCell,
MatSortHeader,
MatCellDef,
MatCell,
MatHeaderRowDef,
MatHeaderRow,
MatRowDef,
MatRow,
MatPaginator
]
})
export class CollectionSheetComponent implements OnInit {
/** Offices Data */
officesData: any;
/** Group Data */
groupsData: any = [];
/** Center Data */
centersData: any = [];
/** Loan Officer Data */
loanOfficerData: any = [];
/** Minimum Date allowed. */
minDate = new Date(2000, 0, 1);
/** Maximum Date allowed. */
maxDate = new Date();
/** Collection Sheet form. */
collectionSheetForm: UntypedFormGroup;
officeId: number | null = null;
meetingFallCenters: MeetingFallCenter[] | null = null;
/**
* Retrieves the offices data from `resolve`.
* @param {FormBuilder} formBuilder Form Builder.
* @param {OrganizationService} collectionsService Organization Service.
* @param {Route} route Route.
* @param {Dates} dateUtils Date Utils to format date.
* @param {Dialog} dialog Dialog component.
* @param {Router} router Router for navigation.
* @param {SettingsService} settingsService Settings Service
*/
constructor(
private formBuilder: UntypedFormBuilder,
private centerService: CentersService,
private collectionsService: CollectionsService,
private organizationService: OrganizationService,
private router: Router,
private route: ActivatedRoute,
private settingsService: SettingsService,
private dateUtils: Dates
) {
this.route.data.subscribe((data: { officesData: any }) => {
this.officesData = data.officesData;
});
}
ngOnInit() {
this.maxDate = this.settingsService.maxFutureDate;
this.createCollectionSheetForm();
this.buildDependencies();
}
/**
* Creates the Individual Collection Sheet Form
*/
createCollectionSheetForm() {
this.collectionSheetForm = this.formBuilder.group({
officeId: [
'',
Validators.required
],
meetingDate: [
new Date(),
Validators.required
],
staffId: [
'',
Validators.required
],
groupId: [''],
centerId: ['']
});
}
/**
* Checks for the office id value change
*/
buildDependencies() {
this.collectionSheetForm.get('officeId').valueChanges.subscribe((officeId: any) => {
this.officeId = officeId;
this.organizationService.getStaffs(officeId).subscribe((response: any) => {
this.loanOfficerData = response;
});
this.organizationService.getCenters(officeId).subscribe((response: any) => {
this.centersData = response;
});
this.organizationService.getGroups(officeId).subscribe((response: any) => {
this.groupsData = response;
});
});
}
previewCollectionSheet() {
const dateFormat = this.settingsService.dateFormat;
const locale = this.settingsService.language.code;
const staffId = this.collectionSheetForm.value.staffId;
const meetingDate = this.dateUtils.formatDate(this.collectionSheetForm.value.meetingDate, dateFormat);
this.centerService
.getAllMeetingFallCenters(this.officeId, staffId, meetingDate, dateFormat, locale)
.subscribe((response: CollectionSheetData[]) => {
if (response.length > 0) {
this.meetingFallCenters = response[0].meetingFallCenters;
const payload = {
calendarId: this.meetingFallCenters[0].collectionMeetingCalendar.calendarInstanceId,
transactionDate: meetingDate,
locale,
dateFormat
};
this.collectionsService
.generateCollectionSheetData(this.meetingFallCenters[0].id, payload)
.subscribe((jlgGroupData: JLGGroupData) => {
console.log(jlgGroupData);
});
}
});
}
}
@@ -7,6 +7,7 @@ import { Route } from '../core/route/route.service';
/** Custom Components */
import { IndividualCollectionSheetComponent } from './individual-collection-sheet/individual-collection-sheet.component';
import { OfficesResolver } from 'app/organization/offices/common-resolvers/offices.resolver';
import { CollectionSheetComponent } from './collection-sheet/collection-sheet.component';
const routes: Routes = [
Route.withShell([
@@ -24,6 +25,18 @@ const routes: Routes = [
resolve: {
officesData: OfficesResolver
}
},
{
path: 'collection-sheet',
data: {
title: 'Collection Sheet',
breadcrumb: 'Collection Sheet',
routeParamBreadcrumb: false
},
component: CollectionSheetComponent,
resolve: {
officesData: OfficesResolver
}
}
]
}
+5 -16
View File
@@ -14,22 +14,6 @@ import { Observable } from 'rxjs';
export class CollectionsService {
constructor(private http: HttpClient) {}
/**
* Returns all the offices
*/
getOffices(): Observable<any> {
return this.http.get(`/offices`);
}
/**
* Returns the loan officer(staffs) for the particular office
* @param {officeId} OfficeId any
*/
getStaffs(officeId: any): Observable<any> {
const httpParams = new HttpParams().set('officeId', officeId).set('status', 'all');
return this.http.get(`/staff`, { params: httpParams });
}
/**
* Retrieves the Collection Sheet Data
* @param {data} data any
@@ -39,6 +23,11 @@ export class CollectionsService {
return this.http.post(`/collectionsheet`, data, { params: httpParams });
}
generateCollectionSheetData(centerId: number, data: any): Observable<any> {
const httpParams = new HttpParams().set('command', 'generateCollectionSheet');
return this.http.post(`/centers/${centerId}`, data, { params: httpParams });
}
/**
* Executes the Save Collection Sheet Data
* @param {data} data any
@@ -16,8 +16,8 @@ import {
MatRowDef,
MatRow
} from '@angular/material/table';
import { UntypedFormGroup, UntypedFormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
import { UntypedFormGroup, UntypedFormBuilder, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
/** Services Import */
import { CollectionsService } from '../collections.service';
@@ -35,6 +35,7 @@ import { SettingsService } from 'app/settings/settings.service';
import { Dates } from 'app/core/utils/dates';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
import { OrganizationService } from 'app/organization/organization.service';
/**
* Individual Collection Sheet
@@ -136,6 +137,7 @@ export class IndividualCollectionSheetComponent implements OnInit {
constructor(
private formBuilder: UntypedFormBuilder,
private collectionsService: CollectionsService,
private organizationService: OrganizationService,
private route: ActivatedRoute,
private dateUtils: Dates,
public dialog: MatDialog,
@@ -182,7 +184,7 @@ export class IndividualCollectionSheetComponent implements OnInit {
*/
buildDependencies() {
this.collectionSheetForm.get('officeId').valueChanges.subscribe((value: any) => {
this.collectionsService.getStaffs(value).subscribe((response: any) => {
this.organizationService.getStaffs(value).subscribe((response: any) => {
this.loanOfficerData = response;
});
});
@@ -0,0 +1,57 @@
import { PaymentTypeOption } from 'app/shared/models/general.model';
export interface CollectionSheetData {
staffId: number;
staffName: string;
meetingFallCenters: MeetingFallCenter[];
}
export interface MeetingFallCenter {
id: number;
accountNo: string;
name: string;
officeId: number;
staffId: number;
staffName: string;
hierarchy: string;
status: Status;
active: boolean;
activationDate: number[];
collectionMeetingCalendar: CollectionMeetingCalendar;
totalCollected: number;
totalOverdue: number;
totaldue: number;
installmentDue: number;
}
export interface CollectionMeetingCalendar {
id: number;
calendarInstanceId: number;
entityId: number;
entityType: Status;
title: string;
startDate: number[];
repeating: boolean;
recurrence: string;
}
export interface Status {
id: number;
code: string;
value: string;
}
export interface JLGGroupData {
dueDate: number[];
loanProducts: any[];
savingsProducts: any[];
groups: any[];
attendanceTypeOptions: AttendanceTypeOption[];
paymentTypeOptions: PaymentTypeOption[];
}
export interface AttendanceTypeOption {
id: number;
code: string;
value: string;
}
@@ -94,6 +94,18 @@
</mat-icon>
<a matLine>{{ 'labels.menus.Checker Inbox and Tasks' | translate }}</a>
</mat-list-item>
<mat-list-item
[routerLink]="['/collections/collection-sheet']"
[matTooltipPosition]="tooltipPosition"
matTooltip="{{ 'tooltips.Collection Sheet' | translate }}"
routerLinkActive="active-menu"
[routerLinkActiveOptions]="{ exact: false }"
>
<mat-icon matListIcon>
<i class="fa fa-tasks"></i>
</mat-icon>
<a matLine>{{ 'labels.menus.Collection Sheet' | translate }}</a>
</mat-list-item>
<mat-list-item
[routerLink]="['/collections/individual-collection-sheet']"
[matTooltipPosition]="tooltipPosition"
@@ -702,6 +702,21 @@ export class OrganizationService {
return this.http.put(`/funds/${fundId}`, fundData);
}
getStaffs(officeId: any): Observable<any> {
const httpParams = new HttpParams().set('officeId', officeId).set('status', 'all');
return this.http.get(`/staff`, { params: httpParams });
}
getCenters(officeId: any): Observable<any> {
const httpParams = new HttpParams().set('officeId', officeId).set('status', 'all');
return this.http.get(`/centers`, { params: httpParams });
}
getGroups(officeId: any): Observable<any> {
const httpParams = new HttpParams().set('officeId', officeId).set('status', 'all');
return this.http.get(`/groups`, { params: httpParams });
}
/*
* @param {any} officeId ID of office to retrieve staff from.
* @returns {Observable<any>} Staff data.