fix: use rent dues in rent schedule (#28)
This commit is contained in:
1 parent
c2afe515c0
commit
a039243d03
4 files changed
+274
-230
No files matched your search
@@ -1,44 +1,39 @@
|
||||
import { Component, computed, inject, signal } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { RentPayment, RentPaymentStatus } from '../../services/rent-payment.service';
|
||||
import { currentRentSchedule, sortRentPayments } from '../../services/rent-payment-schedule';
|
||||
import { RentDue } from '../../services/lease.service';
|
||||
import { WorkspaceService } from '../../services/workspace.service';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { RentPaymentValidationDialog } from '../rent-payment-validation-dialog/rent-payment-validation-dialog';
|
||||
import { DatePipe } from '@angular/common';
|
||||
|
||||
@Component({
|
||||
selector: 'app-rent-payments-page',
|
||||
imports: [MatButtonModule, MatProgressBarModule, MatProgressSpinnerModule, DatePipe],
|
||||
imports: [MatButtonModule, MatProgressBarModule, DatePipe],
|
||||
templateUrl: './rent-payments-page.html',
|
||||
styleUrl: './rent-payments-page.css',
|
||||
})
|
||||
export class RentPaymentsPage {
|
||||
readonly workspace = inject(WorkspaceService);
|
||||
private readonly dialog = inject(MatDialog);
|
||||
readonly showFullSchedule = signal(false);
|
||||
readonly displayedPayments = computed(() =>
|
||||
readonly displayedRentDues = computed(() =>
|
||||
this.showFullSchedule()
|
||||
? sortRentPayments(this.workspace.rentPayments())
|
||||
: currentRentSchedule(this.workspace.rentPayments()),
|
||||
? sortRentDues(this.workspace.rentDues())
|
||||
: currentRentDues(this.workspace.rentDues()),
|
||||
);
|
||||
readonly contentReady = computed(
|
||||
() =>
|
||||
this.workspace.rentPaymentLoadState().loaded &&
|
||||
this.workspace.rentDueLoadState().loaded &&
|
||||
this.workspace.leaseLoadState().loaded &&
|
||||
this.workspace.apartmentLoadState().loaded,
|
||||
);
|
||||
readonly contentLoading = computed(
|
||||
() =>
|
||||
this.workspace.rentPaymentLoadState().loading ||
|
||||
this.workspace.rentDueLoadState().loading ||
|
||||
this.workspace.leaseLoadState().loading ||
|
||||
this.workspace.apartmentLoadState().loading,
|
||||
);
|
||||
readonly loadError = computed(
|
||||
() =>
|
||||
this.workspace.rentPaymentLoadState().error ||
|
||||
this.workspace.rentDueLoadState().error ||
|
||||
this.workspace.leaseLoadState().error ||
|
||||
this.workspace.apartmentLoadState().error,
|
||||
);
|
||||
@@ -47,9 +42,12 @@ export class RentPaymentsPage {
|
||||
this.load();
|
||||
}
|
||||
|
||||
apartmentName(payment: RentPayment): string {
|
||||
const lease = this.workspace.leases().find((item) => item.id === payment.leaseId);
|
||||
return this.workspace.apartments().find((item) => item.id === lease?.apartmentId)?.name ?? 'Bien inconnu';
|
||||
apartmentName(rentDue: RentDue): string {
|
||||
const lease = this.workspace.leases().find((item) => item.id === rentDue.leaseId);
|
||||
return (
|
||||
this.workspace.apartments().find((item) => item.id === lease?.apartmentId)?.name ??
|
||||
'Bien inconnu'
|
||||
);
|
||||
}
|
||||
|
||||
monthLabel(month: string): string {
|
||||
@@ -58,12 +56,18 @@ export class RentPaymentsPage {
|
||||
);
|
||||
}
|
||||
|
||||
statusLabel(status: RentPaymentStatus): string {
|
||||
return { PAID: 'Payé', LATE: 'En retard', UPCOMING: 'À venir' }[status];
|
||||
settlementLabel(settlement: RentDue['settlement']): string {
|
||||
return { UNPAID: 'Non réglée', PARTIALLY_PAID: 'Partiellement réglée', PAID: 'Réglée' }[
|
||||
settlement
|
||||
];
|
||||
}
|
||||
|
||||
timingLabel(timing: RentDue['timing']): string {
|
||||
return { UPCOMING: 'À venir', OVERDUE: 'En retard' }[timing];
|
||||
}
|
||||
|
||||
load(): void {
|
||||
this.workspace.loadRentPayments();
|
||||
this.workspace.loadRentDues();
|
||||
this.workspace.loadLeases();
|
||||
this.workspace.loadApartments();
|
||||
}
|
||||
@@ -71,19 +75,33 @@ export class RentPaymentsPage {
|
||||
toggleSchedule(): void {
|
||||
this.showFullSchedule.update((showFullSchedule) => !showFullSchedule);
|
||||
}
|
||||
}
|
||||
|
||||
markPaid(payment: RentPayment): void {
|
||||
this.dialog
|
||||
.open(RentPaymentValidationDialog, {
|
||||
data: { rent: payment.rent, charges: payment.charges },
|
||||
width: '400px',
|
||||
})
|
||||
.afterClosed()
|
||||
.subscribe((result) => {
|
||||
if (result) {
|
||||
this.workspace.markRentPaymentPaid(payment.id, result.rent, result.charges);
|
||||
}
|
||||
});
|
||||
function sortRentDues(rentDues: readonly RentDue[]): RentDue[] {
|
||||
return [...rentDues].sort((left, right) => left.dueDate.localeCompare(right.dueDate));
|
||||
}
|
||||
|
||||
function currentRentDues(rentDues: readonly RentDue[]): RentDue[] {
|
||||
const rentDuesByLease = new Map<string, RentDue[]>();
|
||||
for (const rentDue of sortRentDues(rentDues)) {
|
||||
const schedule = rentDuesByLease.get(rentDue.leaseId);
|
||||
if (schedule) schedule.push(rentDue);
|
||||
else rentDuesByLease.set(rentDue.leaseId, [rentDue]);
|
||||
}
|
||||
return sortRentDues([...rentDuesByLease.values()].flatMap(currentSchedule));
|
||||
}
|
||||
|
||||
function currentSchedule(schedule: RentDue[]): RentDue[] {
|
||||
const nextDueIndex = schedule.findIndex((rentDue) => rentDue.timing === 'UPCOMING');
|
||||
const throughNextDue = nextDueIndex === -1 ? schedule : schedule.slice(0, nextDueIndex + 1);
|
||||
const lastPaidIndex = throughNextDue.reduce(
|
||||
(lastPaid, rentDue, index) => (rentDue.settlement === 'PAID' ? index : lastPaid),
|
||||
-1,
|
||||
);
|
||||
const firstUnpaidIndex = throughNextDue.findIndex((rentDue) => rentDue.settlement !== 'PAID');
|
||||
const firstVisibleIndex = Math.min(
|
||||
...[lastPaidIndex, firstUnpaidIndex].filter((index) => index !== -1),
|
||||
);
|
||||
|
||||
return firstVisibleIndex === Infinity ? [] : throughNextDue.slice(firstVisibleIndex);
|
||||
}
|
||||
Reference in new issue
Block a user