feat(expense-snapshot): frontend model + api service with tests
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,21 @@
|
|||||||
|
import { ExpenseLineInput, FunctionalClass } from './expense.model';
|
||||||
|
|
||||||
|
export interface ExpenseSnapshotLineDto {
|
||||||
|
categoryGroupId: number; categoryGroupName: string;
|
||||||
|
subCategoryId: number; subCategoryName: string;
|
||||||
|
functionalClass: FunctionalClass | null; amount: number; description: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ExpenseSnapshotDto {
|
||||||
|
id: number; name: string; ministryId: number; ministryName: string;
|
||||||
|
description: string; vendorName: string | null; checkNumber: string | null; notes: string | null;
|
||||||
|
totalAmount: number; lineCount: number;
|
||||||
|
createdByName: string | null; createdAt: string;
|
||||||
|
lines: ExpenseSnapshotLineDto[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateExpenseSnapshotRequest {
|
||||||
|
name: string; ministryId: number; lines: ExpenseLineInput[];
|
||||||
|
description: string; vendorName: string | null; checkNumber: string | null; notes: string | null;
|
||||||
|
}
|
||||||
|
export type UpdateExpenseSnapshotRequest = CreateExpenseSnapshotRequest;
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
|
||||||
|
import { ExpenseSnapshotApiService } from './expense-snapshot-api.service';
|
||||||
|
import { ApiConfigService } from '../../../core/services/api-config.service';
|
||||||
|
import { CreateExpenseSnapshotRequest } from '../models/expense-snapshot.model';
|
||||||
|
|
||||||
|
describe('ExpenseSnapshotApiService', () => {
|
||||||
|
let service: ExpenseSnapshotApiService;
|
||||||
|
let httpMock: HttpTestingController;
|
||||||
|
const base = 'http://test/api/expense-snapshots';
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [HttpClientTestingModule],
|
||||||
|
providers: [
|
||||||
|
ExpenseSnapshotApiService,
|
||||||
|
{ provide: ApiConfigService, useValue: { getApiUrl: () => base } },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
service = TestBed.inject(ExpenseSnapshotApiService);
|
||||||
|
httpMock = TestBed.inject(HttpTestingController);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => httpMock.verify());
|
||||||
|
|
||||||
|
it('getAll() GETs the collection endpoint', () => {
|
||||||
|
service.getAll().subscribe();
|
||||||
|
const req = httpMock.expectOne(base);
|
||||||
|
expect(req.request.method).toBe('GET');
|
||||||
|
req.flush([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('create() POSTs the request body', () => {
|
||||||
|
const body: CreateExpenseSnapshotRequest = {
|
||||||
|
name: 'Rent', ministryId: 1, description: 'Office rent',
|
||||||
|
vendorName: 'Landlord X', checkNumber: null, notes: null,
|
||||||
|
lines: [{ categoryGroupId: 1, subCategoryId: 1, amount: 1200, functionalClass: null, description: null }],
|
||||||
|
};
|
||||||
|
service.create(body).subscribe();
|
||||||
|
const req = httpMock.expectOne(base);
|
||||||
|
expect(req.request.method).toBe('POST');
|
||||||
|
expect(req.request.body.name).toBe('Rent');
|
||||||
|
req.flush({ id: 7 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('delete() DELETEs by id', () => {
|
||||||
|
service.delete(9).subscribe();
|
||||||
|
const req = httpMock.expectOne(`${base}/9`);
|
||||||
|
expect(req.request.method).toBe('DELETE');
|
||||||
|
req.flush(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { ApiConfigService } from '../../../core/services/api-config.service';
|
||||||
|
import {
|
||||||
|
ExpenseSnapshotDto, CreateExpenseSnapshotRequest, UpdateExpenseSnapshotRequest,
|
||||||
|
} from '../models/expense-snapshot.model';
|
||||||
|
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class ExpenseSnapshotApiService {
|
||||||
|
private readonly endpoint: string;
|
||||||
|
constructor(private http: HttpClient, apiConfig: ApiConfigService) {
|
||||||
|
this.endpoint = apiConfig.getApiUrl('expense-snapshots');
|
||||||
|
}
|
||||||
|
getAll(): Observable<ExpenseSnapshotDto[]> {
|
||||||
|
return this.http.get<ExpenseSnapshotDto[]>(this.endpoint);
|
||||||
|
}
|
||||||
|
getById(id: number): Observable<ExpenseSnapshotDto> {
|
||||||
|
return this.http.get<ExpenseSnapshotDto>(`${this.endpoint}/${id}`);
|
||||||
|
}
|
||||||
|
create(r: CreateExpenseSnapshotRequest): Observable<{ id: number }> {
|
||||||
|
return this.http.post<{ id: number }>(this.endpoint, r);
|
||||||
|
}
|
||||||
|
update(id: number, r: UpdateExpenseSnapshotRequest): Observable<void> {
|
||||||
|
return this.http.put<void>(`${this.endpoint}/${id}`, r);
|
||||||
|
}
|
||||||
|
delete(id: number): Observable<void> {
|
||||||
|
return this.http.delete<void>(`${this.endpoint}/${id}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user