feat(1099): add 1099 recipient picker to expense form
Add optional payeeId to CreateExpenseRequest + ExpenseListItemDto frontend models. In the expense form dialog: inject Payee1099ApiService, load active payees on init, add payeeId to the form state, pre-populate it from expense.payeeId in edit mode, and include it in the emitSave payload. Render a "1099 Recipient / 1099 收款人" Kendo DropdownList (textField=legalName, valueField=id, [valuePrimitive]="true", md:col-span-2) inside the vendor-mode ng-container below Vendor Name and Check #. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+7
-1
@@ -158,7 +158,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- Vendor mode: vendor name + check number -->
|
<!-- Vendor mode: vendor name + check number + optional 1099 recipient -->
|
||||||
<ng-container *ngIf="mode === 'vendor'">
|
<ng-container *ngIf="mode === 'vendor'">
|
||||||
<label class="flex flex-col gap-1">Vendor Name
|
<label class="flex flex-col gap-1">Vendor Name
|
||||||
<kendo-textbox [(ngModel)]="form.vendorName" placeholder="Payee / vendor name"></kendo-textbox>
|
<kendo-textbox [(ngModel)]="form.vendorName" placeholder="Payee / vendor name"></kendo-textbox>
|
||||||
@@ -166,6 +166,12 @@
|
|||||||
<label class="flex flex-col gap-1">Check #
|
<label class="flex flex-col gap-1">Check #
|
||||||
<kendo-textbox [(ngModel)]="form.checkNumber" placeholder="Check number (optional)"></kendo-textbox>
|
<kendo-textbox [(ngModel)]="form.checkNumber" placeholder="Check number (optional)"></kendo-textbox>
|
||||||
</label>
|
</label>
|
||||||
|
<label class="flex flex-col gap-1 md:col-span-2">1099 Recipient / 1099 收款人 <span class="text-gray-400 font-normal">(optional)</span>
|
||||||
|
<kendo-dropdownlist [data]="payees" textField="legalName" valueField="id" [valuePrimitive]="true"
|
||||||
|
[defaultItem]="{ id: null, legalName: '— none —' }"
|
||||||
|
[(ngModel)]="form.payeeId">
|
||||||
|
</kendo-dropdownlist>
|
||||||
|
</label>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<!-- Reimbursement mode: receipt file input -->
|
<!-- Reimbursement mode: receipt file input -->
|
||||||
|
|||||||
+9
@@ -15,6 +15,8 @@ import { ExpenseSnapshotDto, CreateExpenseSnapshotRequest } from '../../models/e
|
|||||||
import { ExpenseAiService } from '../../services/expense-ai.service';
|
import { ExpenseAiService } from '../../services/expense-ai.service';
|
||||||
import { MemberApiService } from '../../../members/services/member-api.service';
|
import { MemberApiService } from '../../../members/services/member-api.service';
|
||||||
import { MemberListItemDto, memberDisplayName } from '../../../members/models/member.model';
|
import { MemberListItemDto, memberDisplayName } from '../../../members/models/member.model';
|
||||||
|
import { Payee1099ApiService } from '../../../payee1099/services/payee1099-api.service';
|
||||||
|
import { Payee1099ListItem } from '../../../payee1099/models/payee1099.model';
|
||||||
import {
|
import {
|
||||||
MinistryDto, ExpenseCategoryGroupDto, ExpenseSubCategoryDto, ExpenseType, CreateExpenseRequest,
|
MinistryDto, ExpenseCategoryGroupDto, ExpenseSubCategoryDto, ExpenseType, CreateExpenseRequest,
|
||||||
ExpenseDto, FunctionalClass, ExpenseAiSuggestion,
|
ExpenseDto, FunctionalClass, ExpenseAiSuggestion,
|
||||||
@@ -66,6 +68,8 @@ export class ExpenseFormDialogComponent implements OnInit, OnDestroy {
|
|||||||
ministries: MinistryDto[] = [];
|
ministries: MinistryDto[] = [];
|
||||||
groups: ExpenseCategoryGroupDto[] = [];
|
groups: ExpenseCategoryGroupDto[] = [];
|
||||||
|
|
||||||
|
payees: Payee1099ListItem[] = [];
|
||||||
|
|
||||||
/** Saved snapshots (vendor mode only) for the "Load from snapshot" picker. */
|
/** Saved snapshots (vendor mode only) for the "Load from snapshot" picker. */
|
||||||
snapshots: ExpenseSnapshotDto[] = [];
|
snapshots: ExpenseSnapshotDto[] = [];
|
||||||
/** Picker binding; reset to null after each apply so the same snapshot can be re-picked. */
|
/** Picker binding; reset to null after each apply so the same snapshot can be re-picked. */
|
||||||
@@ -95,6 +99,7 @@ export class ExpenseFormDialogComponent implements OnInit, OnDestroy {
|
|||||||
vendorName: '',
|
vendorName: '',
|
||||||
checkNumber: '',
|
checkNumber: '',
|
||||||
memberId: null as number | null,
|
memberId: null as number | null,
|
||||||
|
payeeId: null as number | null,
|
||||||
expenseDate: new Date(),
|
expenseDate: new Date(),
|
||||||
};
|
};
|
||||||
/** At least one line always; "+ Add line" appends, each line is independently removable down to one. */
|
/** At least one line always; "+ Add line" appends, each line is independently removable down to one. */
|
||||||
@@ -133,11 +138,13 @@ export class ExpenseFormDialogComponent implements OnInit, OnDestroy {
|
|||||||
private expenseApi: ExpenseApiService,
|
private expenseApi: ExpenseApiService,
|
||||||
private snapshotApi: ExpenseSnapshotApiService,
|
private snapshotApi: ExpenseSnapshotApiService,
|
||||||
private aiApi: ExpenseAiService,
|
private aiApi: ExpenseAiService,
|
||||||
|
private payeeApi: Payee1099ApiService,
|
||||||
private sanitizer: DomSanitizer,
|
private sanitizer: DomSanitizer,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.ministryApi.getAll().subscribe(m => (this.ministries = m));
|
this.ministryApi.getAll().subscribe(m => (this.ministries = m));
|
||||||
|
this.payeeApi.getAll(false).subscribe(list => (this.payees = list));
|
||||||
if (this.showSnapshotTools) this.loadSnapshots();
|
if (this.showSnapshotTools) this.loadSnapshots();
|
||||||
this.catApi.getAll(false).subscribe(groups => {
|
this.catApi.getAll(false).subscribe(groups => {
|
||||||
this.groups = groups;
|
this.groups = groups;
|
||||||
@@ -169,6 +176,7 @@ export class ExpenseFormDialogComponent implements OnInit, OnDestroy {
|
|||||||
vendorName: expense.vendorName ?? '',
|
vendorName: expense.vendorName ?? '',
|
||||||
checkNumber: expense.checkNumber ?? '',
|
checkNumber: expense.checkNumber ?? '',
|
||||||
memberId: expense.memberId,
|
memberId: expense.memberId,
|
||||||
|
payeeId: expense.payeeId ?? null,
|
||||||
expenseDate: new Date(year, month - 1, day),
|
expenseDate: new Date(year, month - 1, day),
|
||||||
};
|
};
|
||||||
this.lines = (expense.lines ?? []).map(l => ({
|
this.lines = (expense.lines ?? []).map(l => ({
|
||||||
@@ -424,6 +432,7 @@ export class ExpenseFormDialogComponent implements OnInit, OnDestroy {
|
|||||||
checkNumber: this.mode === 'vendor' ? (this.form.checkNumber || null) : null,
|
checkNumber: this.mode === 'vendor' ? (this.form.checkNumber || null) : null,
|
||||||
expenseDate,
|
expenseDate,
|
||||||
notes: null,
|
notes: null,
|
||||||
|
payeeId: this.form.payeeId,
|
||||||
};
|
};
|
||||||
// The request and receipt are snapshotted here, so resetting the form right
|
// The request and receipt are snapshotted here, so resetting the form right
|
||||||
// after emitting is safe even though the parent saves asynchronously.
|
// after emitting is safe even though the parent saves asynchronously.
|
||||||
|
|||||||
Reference in New Issue
Block a user