feat: add time zone support for rent payment and refund processes

- Updated API requests and responses to include time zone information.
- Modified backend services to handle time zone for received and refunded dates.
- Adjusted frontend components to use local date formatting based on the user's time zone.
- Enhanced tests to validate time zone handling in rent payment and refund scenarios.
This commit is contained in:
nessar committed 2026-08-17 14:18:15 +02:00
1 parent b9e99348f7
commit d7a7aff3ee
15 files changed
+150 -60

No files matched your search

@@ -8,7 +8,7 @@ import { RentPaymentRefundDialog } from './rent-payment-refund-dialog/rent-payme
import { RentPaymentReversalDialog } from './rent-payment-reversal-dialog/rent-payment-reversal-dialog';
import { RentPaymentValidationDialog } from './rent-payment-validation-dialog/rent-payment-validation-dialog';
import { RentDue } from '../services/lease.service';
import { utcIsoDate } from '../services/rent-payment-format';
import { browserTimeZone, localIsoDate } from '../services/rent-payment-format';
import { RentPayment, RentPaymentService } from '../services/rent-payment.service';
describe('rent payment dialogs', () => {
@@ -24,8 +24,10 @@ describe('rent payment dialogs', () => {
vi.clearAllMocks();
});
it('uses UTC for payment and refund date defaults', () => {
expect(utcIsoDate(new Date('2026-07-18T23:30:00Z'))).toBe('2026-07-18');
it('uses the user local date for payment and refund defaults', () => {
const instant = new Date('2026-07-19T06:30:00Z');
expect(localIsoDate(instant, 'America/Los_Angeles')).toBe('2026-07-18');
expect(localIsoDate(instant, 'Europe/Paris')).toBe('2026-07-19');
});
it('records one received amount and exposes its surplus outcome', () => {
@@ -44,6 +46,7 @@ describe('rent payment dialogs', () => {
operationId: expect.any(String),
rentDueId: 'due-1',
rentDueVersion: 2,
timeZone: browserTimeZone,
receivedAt: component.today,
amount: 950,
});
@@ -102,7 +105,7 @@ describe('rent payment dialogs', () => {
component.submit();
expect(payments.refund).toHaveBeenCalledWith(
'payment-1',
expect.objectContaining({ amount: 100, paymentVersion: 3 }),
expect.objectContaining({ amount: 100, paymentVersion: 3, timeZone: browserTimeZone }),
);
});
@@ -14,7 +14,7 @@ import { MatInputModule } from '@angular/material/input';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { finalize } from 'rxjs';
import { httpErrorMessage } from '../../services/http-error-message';
import { formatEuro, utcIsoDate } from '../../services/rent-payment-format';
import { browserTimeZone, formatEuro, localIsoDate } from '../../services/rent-payment-format';
import { RentPayment, RentPaymentService } from '../../services/rent-payment.service';
import { isRentPaymentDataStale, RENT_PAYMENT_DATA_STALE } from '../rent-payment-dialog-result';
@@ -36,7 +36,7 @@ export class RentPaymentRefundDialog {
private readonly dialogRef = inject(MatDialogRef<RentPaymentRefundDialog>);
private readonly payments = inject(RentPaymentService);
private readonly operationId = crypto.randomUUID();
readonly today = utcIsoDate();
readonly today = localIsoDate();
readonly saving = signal(false);
readonly error = signal('');
readonly formatEuro = formatEuro;
@@ -65,6 +65,7 @@ export class RentPaymentRefundDialog {
.refund(this.payment.id, {
operationId: this.operationId,
paymentVersion: this.payment.version,
timeZone: browserTimeZone,
...this.form.getRawValue(),
})
.pipe(finalize(() => this.saving.set(false)))
@@ -16,7 +16,7 @@ import { toSignal } from '@angular/core/rxjs-interop';
import { finalize } from 'rxjs';
import { RentDue } from '../../services/lease.service';
import { httpErrorMessage } from '../../services/http-error-message';
import { formatEuro, utcIsoDate } from '../../services/rent-payment-format';
import { browserTimeZone, formatEuro, localIsoDate } from '../../services/rent-payment-format';
import { RentPaymentService } from '../../services/rent-payment.service';
import { isRentPaymentDataStale, RENT_PAYMENT_DATA_STALE } from '../rent-payment-dialog-result';
@@ -43,7 +43,7 @@ export class RentPaymentValidationDialog {
private readonly dialogRef = inject(MatDialogRef<RentPaymentValidationDialog>);
private readonly payments = inject(RentPaymentService);
private readonly operationId = crypto.randomUUID();
readonly today = utcIsoDate();
readonly today = localIsoDate();
readonly saving = signal(false);
readonly error = signal('');
readonly formatEuro = formatEuro;
@@ -82,6 +82,7 @@ export class RentPaymentValidationDialog {
operationId: this.operationId,
rentDueId: this.data.rentDue.id,
rentDueVersion: this.data.rentDue.version,
timeZone: browserTimeZone,
...this.form.getRawValue(),
})
.pipe(finalize(() => this.saving.set(false)))
@@ -9,6 +9,8 @@ export function formatEuro(value: number): string {
return euroFormatter.format(value);
}
export function utcIsoDate(date = new Date()): string {
return date.toISOString().slice(0, 10);
export const browserTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
export function localIsoDate(date = new Date(), timeZone = browserTimeZone): string {
return new Intl.DateTimeFormat('sv-SE', { timeZone }).format(date);
}
@@ -19,6 +19,7 @@ describe('RentPaymentService', () => {
rentDueVersion: 2,
receivedAt: '2026-01-05',
amount: 900,
timeZone: 'America/Los_Angeles',
};
const allocation = {
operationId: 'operation-2',
@@ -32,6 +33,7 @@ describe('RentPaymentService', () => {
paymentVersion: 2,
amount: 25,
refundedAt: '2026-01-06',
timeZone: 'America/Los_Angeles',
};
service.getRentPayments().subscribe();
@@ -47,6 +47,7 @@ export interface RecordRentPaymentRequest {
rentDueVersion: number;
receivedAt: string;
amount: number;
timeZone: string;
}
export interface AllocateRentPaymentRequest {
@@ -62,6 +63,7 @@ export interface RefundRentPaymentRequest {
paymentVersion: number;
amount: number;
refundedAt: string;
timeZone: string;
}
@Injectable({ providedIn: 'root' })