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`; }
}