feat(expense): add frontend models + API services

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Chris Chen
2026-05-29 18:40:22 -07:00
parent 9933c180b7
commit 04b05617b8
5 changed files with 153 additions and 0 deletions
@@ -0,0 +1,45 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import { ApiConfigService } from '../../../core/services/api-config.service';
import {
PagedResult, ExpenseListItemDto, ExpenseDto, CreateExpenseRequest, UpdateExpenseRequest,
RejectExpenseRequest, PayExpenseRequest,
} from '../models/expense.model';
export interface ExpenseQuery {
page?: number; pageSize?: number; search?: string; ministryId?: number;
categoryGroupId?: number; status?: string; from?: string; to?: string;
}
@Injectable({ providedIn: 'root' })
export class ExpenseApiService {
private readonly endpoint: string;
constructor(private http: HttpClient, apiConfig: ApiConfigService) {
this.endpoint = apiConfig.getApiUrl('expenses');
}
private toParams(q: Record<string, unknown>): HttpParams {
let p = new HttpParams();
for (const [k, v] of Object.entries(q)) if (v !== undefined && v !== null && v !== '') p = p.set(k, String(v));
return p;
}
getPaged(q: ExpenseQuery): Observable<PagedResult<ExpenseListItemDto>> {
return this.http.get<PagedResult<ExpenseListItemDto>>(this.endpoint, { params: this.toParams(q as Record<string, unknown>) });
}
getMine(status?: string, page = 1, pageSize = 50): Observable<PagedResult<ExpenseListItemDto>> {
return this.http.get<PagedResult<ExpenseListItemDto>>(`${this.endpoint}/mine`, { params: this.toParams({ status, page, pageSize }) });
}
getById(id: number): Observable<ExpenseDto> { return this.http.get<ExpenseDto>(`${this.endpoint}/${id}`); }
create(r: CreateExpenseRequest): Observable<{ id: number }> { return this.http.post<{ id: number }>(this.endpoint, r); }
update(id: number, r: UpdateExpenseRequest): Observable<void> { return this.http.put<void>(`${this.endpoint}/${id}`, r); }
delete(id: number): Observable<void> { return this.http.delete<void>(`${this.endpoint}/${id}`); }
submit(id: number): Observable<void> { return this.http.post<void>(`${this.endpoint}/${id}/submit`, {}); }
approve(id: number): Observable<void> { return this.http.post<void>(`${this.endpoint}/${id}/approve`, {}); }
reject(id: number, r: RejectExpenseRequest): Observable<void> { return this.http.post<void>(`${this.endpoint}/${id}/reject`, r); }
pay(id: number, r: PayExpenseRequest): Observable<void> { return this.http.post<void>(`${this.endpoint}/${id}/pay`, r); }
uploadReceipt(id: number, file: File): Observable<void> {
const form = new FormData(); form.append('file', file);
return this.http.post<void>(`${this.endpoint}/${id}/receipt`, form);
}
receiptUrl(id: number): string { return `${this.endpoint}/${id}/receipt`; }
}
@@ -0,0 +1,25 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import { ApiConfigService } from '../../../core/services/api-config.service';
import {
ExpenseCategoryGroupDto, CreateExpenseGroupRequest, UpdateExpenseGroupRequest,
CreateExpenseSubCategoryRequest, UpdateExpenseSubCategoryRequest,
} from '../models/expense.model';
@Injectable({ providedIn: 'root' })
export class ExpenseCategoryApiService {
private readonly endpoint: string;
constructor(private http: HttpClient, apiConfig: ApiConfigService) {
this.endpoint = apiConfig.getApiUrl('expense-categories');
}
getAll(includeInactive = false): Observable<ExpenseCategoryGroupDto[]> {
return this.http.get<ExpenseCategoryGroupDto[]>(this.endpoint, { params: new HttpParams().set('includeInactive', includeInactive) });
}
createGroup(r: CreateExpenseGroupRequest): Observable<{ id: number }> { return this.http.post<{ id: number }>(`${this.endpoint}/groups`, r); }
updateGroup(id: number, r: UpdateExpenseGroupRequest): Observable<void> { return this.http.put<void>(`${this.endpoint}/groups/${id}`, r); }
deactivateGroup(id: number): Observable<void> { return this.http.delete<void>(`${this.endpoint}/groups/${id}`); }
createSub(r: CreateExpenseSubCategoryRequest): Observable<{ id: number }> { return this.http.post<{ id: number }>(`${this.endpoint}/subcategories`, r); }
updateSub(id: number, r: UpdateExpenseSubCategoryRequest): Observable<void> { return this.http.put<void>(`${this.endpoint}/subcategories/${id}`, r); }
deactivateSub(id: number): Observable<void> { return this.http.delete<void>(`${this.endpoint}/subcategories/${id}`); }
}
@@ -0,0 +1,16 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import { ApiConfigService } from '../../../core/services/api-config.service';
import { MinistryDto } from '../models/expense.model';
@Injectable({ providedIn: 'root' })
export class MinistryApiService {
private readonly endpoint: string;
constructor(private http: HttpClient, apiConfig: ApiConfigService) {
this.endpoint = apiConfig.getApiUrl('ministries');
}
getAll(includeInactive = false): Observable<MinistryDto[]> {
return this.http.get<MinistryDto[]>(this.endpoint, { params: new HttpParams().set('includeInactive', includeInactive) });
}
}
@@ -0,0 +1,21 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import { ApiConfigService } from '../../../core/services/api-config.service';
import { MonthlyStatementDto, CreateMonthlyStatementRequest, UpdateMonthlyStatementRequest } from '../models/expense.model';
@Injectable({ providedIn: 'root' })
export class MonthlyStatementApiService {
private readonly endpoint: string;
constructor(private http: HttpClient, apiConfig: ApiConfigService) {
this.endpoint = apiConfig.getApiUrl('monthly-statements');
}
getAll(year?: number): Observable<MonthlyStatementDto[]> {
let p = new HttpParams(); if (year) p = p.set('year', year);
return this.http.get<MonthlyStatementDto[]>(this.endpoint, { params: p });
}
getById(id: number): Observable<MonthlyStatementDto> { return this.http.get<MonthlyStatementDto>(`${this.endpoint}/${id}`); }
create(r: CreateMonthlyStatementRequest): Observable<{ id: number }> { return this.http.post<{ id: number }>(this.endpoint, r); }
update(id: number, r: UpdateMonthlyStatementRequest): Observable<void> { return this.http.put<void>(`${this.endpoint}/${id}`, r); }
finalize(id: number): Observable<void> { return this.http.post<void>(`${this.endpoint}/${id}/finalize`, {}); }
}