771889a99a
In the recipient dialog, changing tax classification on a NEW record sets the 1099-tracked default: CCorp/SCorp default to NOT tracked (spec §2.1/§2.3), others to tracked. Only applies until the user manually toggles it; never overrides an explicit choice or an existing saved value on edit. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
184 lines
8.6 KiB
HTML
184 lines
8.6 KiB
HTML
<div class="page">
|
|
<ng-template appPageHeaderActions>
|
|
<label class="inactive-toggle">
|
|
<input type="checkbox" [(ngModel)]="includeInactive" (change)="load()" /> Show inactive / 顯示停用
|
|
</label>
|
|
<button kendoButton themeColor="primary"
|
|
*appHasPermission="{ module: 'Form1099', action: 'write' }"
|
|
(click)="openNew()">+ New Recipient / 新增收款人</button>
|
|
</ng-template>
|
|
|
|
<div class="hint-text-sm">Click a name to edit · right-click a row for actions / 點選名稱編輯 · 右鍵顯示動作</div>
|
|
|
|
<!-- Desktop grid -->
|
|
<div class="hidden md:block">
|
|
<kendo-grid class="clickable-rows" [data]="recipients" [loading]="loading"
|
|
(cellClick)="onCellClick($event)">
|
|
<kendo-grid-column field="legalName" title="Legal Name / 法定名稱">
|
|
<ng-template kendoGridCellTemplate let-r>
|
|
<span class="legal-name">{{ r.legalName }}</span>
|
|
<span *ngIf="r.displayName" class="display-name"> ({{ r.displayName }})</span>
|
|
</ng-template>
|
|
</kendo-grid-column>
|
|
<kendo-grid-column field="memberName" title="Member / 會友">
|
|
<ng-template kendoGridCellTemplate let-r>{{ r.memberName || '—' }}</ng-template>
|
|
</kendo-grid-column>
|
|
<kendo-grid-column field="taxClassification" title="Tax Class / 稅務分類" [width]="150"></kendo-grid-column>
|
|
<kendo-grid-column title="TIN" [width]="120">
|
|
<ng-template kendoGridCellTemplate let-r>{{ r.tinLast4 ? '***-**-' + r.tinLast4 : '—' }}</ng-template>
|
|
</kendo-grid-column>
|
|
<kendo-grid-column field="w9Status" title="W-9" [width]="120">
|
|
<ng-template kendoGridCellTemplate let-r>
|
|
<span class="badge" [ngClass]="'badge-' + r.w9Status.toLowerCase()">{{ r.w9Status }}</span>
|
|
</ng-template>
|
|
</kendo-grid-column>
|
|
<kendo-grid-column field="is1099Tracked" title="1099 Tracked" [width]="120">
|
|
<ng-template kendoGridCellTemplate let-r>{{ r.is1099Tracked ? 'Yes' : 'No' }}</ng-template>
|
|
</kendo-grid-column>
|
|
<kendo-grid-column field="isActive" title="Active" [width]="90">
|
|
<ng-template kendoGridCellTemplate let-r>{{ r.isActive ? 'Yes' : 'No' }}</ng-template>
|
|
</kendo-grid-column>
|
|
</kendo-grid>
|
|
<kendo-contextmenu #rowMenu [items]="rowMenuItems" (select)="onRowMenuSelect($event)"></kendo-contextmenu>
|
|
</div>
|
|
|
|
<!-- Mobile cards -->
|
|
<div class="md:hidden flex flex-col gap-3">
|
|
<div *ngFor="let r of recipients" class="rounded border p-3" (click)="openEdit(r)">
|
|
<div class="flex justify-between items-start gap-2">
|
|
<div class="font-semibold">{{ r.legalName }}</div>
|
|
<span class="badge" [ngClass]="'badge-' + r.w9Status.toLowerCase()">{{ r.w9Status }}</span>
|
|
</div>
|
|
<div *ngIf="r.displayName" class="text-sm text-gray-500">{{ r.displayName }}</div>
|
|
<div class="text-sm flex justify-between"><span>Member / 會友</span><span>{{ r.memberName || '—' }}</span></div>
|
|
<div class="text-sm flex justify-between"><span>Tax Class</span><span>{{ r.taxClassification }}</span></div>
|
|
<div class="text-sm flex justify-between"><span>TIN</span><span>{{ r.tinLast4 ? '***-**-' + r.tinLast4 : '—' }}</span></div>
|
|
<div class="text-sm flex justify-between"><span>1099 Tracked</span><span>{{ r.is1099Tracked ? 'Yes' : 'No' }}</span></div>
|
|
<div class="text-sm flex justify-between"><span>Active</span><span>{{ r.isActive ? 'Yes' : 'No' }}</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- New / Edit dialog -->
|
|
<kendo-dialog *ngIf="dialogOpen"
|
|
[title]="editingId != null ? 'Edit Recipient / 編輯收款人' : 'New Recipient / 新增收款人'"
|
|
(close)="dialogOpen = false"
|
|
[width]="720" [maxWidth]="'95vw'">
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-4 gap-y-3">
|
|
|
|
<label class="flex flex-col gap-1">
|
|
Legal Name / 法定名稱 *
|
|
<kendo-textbox [(ngModel)]="form.legalName"></kendo-textbox>
|
|
</label>
|
|
<label class="flex flex-col gap-1">
|
|
Display Name / 顯示名稱
|
|
<kendo-textbox [(ngModel)]="form.displayName"></kendo-textbox>
|
|
</label>
|
|
|
|
<label class="flex flex-col gap-1 md:col-span-2">
|
|
Linked Member / 連結會友
|
|
<kendo-dropdownlist
|
|
[data]="memberResults"
|
|
textField="displayName" valueField="id" [valuePrimitive]="true"
|
|
[filterable]="true" (filterChange)="onMemberFilter($event)"
|
|
[defaultItem]="{ id: null, displayName: '(None / 無)' }"
|
|
[(ngModel)]="form.memberId">
|
|
</kendo-dropdownlist>
|
|
</label>
|
|
|
|
<label class="flex flex-col gap-1">
|
|
Tax Classification / 稅務分類
|
|
<kendo-dropdownlist [data]="taxClassifications" [(ngModel)]="form.taxClassification"
|
|
(valueChange)="onTaxClassificationChange($event)"></kendo-dropdownlist>
|
|
</label>
|
|
<label class="flex items-center gap-2 md:mt-6">
|
|
<kendo-switch [(ngModel)]="form.is1099Tracked" (valueChange)="onTrackedToggle()"></kendo-switch>
|
|
<span>1099 Tracked / 列入 1099</span>
|
|
</label>
|
|
|
|
<label class="flex flex-col gap-1">
|
|
TIN Type / 稅號類型
|
|
<kendo-dropdownlist [data]="tinTypes" [(ngModel)]="form.tinType"></kendo-dropdownlist>
|
|
</label>
|
|
<label class="flex flex-col gap-1">
|
|
TIN / 稅號
|
|
<kendo-textbox [(ngModel)]="form.tin"
|
|
[placeholder]="editingId != null && editingTinLast4 ? '***-**-' + editingTinLast4 : ''"></kendo-textbox>
|
|
<span *ngIf="editingId != null" class="hint-text-sm">Leave blank to keep the existing TIN / 留空則保留現有稅號</span>
|
|
<div *ngIf="editingId != null" class="flex flex-col gap-1">
|
|
<button kendoButton type="button" fillMode="link" class="self-start"
|
|
*appHasPermission="{ module: 'Form1099', action: 'write' }"
|
|
(click)="revealTin()">Reveal full TIN / 顯示完整 TIN</button>
|
|
<span *ngIf="revealedTin" class="font-mono">{{ revealedTin }}</span>
|
|
</div>
|
|
</label>
|
|
|
|
<label class="flex flex-col gap-1 md:col-span-2">
|
|
Address Line 1 / 地址 1
|
|
<kendo-textbox [(ngModel)]="form.addressLine1"></kendo-textbox>
|
|
</label>
|
|
<label class="flex flex-col gap-1 md:col-span-2">
|
|
Address Line 2 / 地址 2
|
|
<kendo-textbox [(ngModel)]="form.addressLine2"></kendo-textbox>
|
|
</label>
|
|
<label class="flex flex-col gap-1">
|
|
City / 城市
|
|
<kendo-textbox [(ngModel)]="form.city"></kendo-textbox>
|
|
</label>
|
|
<div class="grid grid-cols-2 gap-x-4">
|
|
<label class="flex flex-col gap-1">
|
|
State / 州
|
|
<kendo-textbox [(ngModel)]="form.state"></kendo-textbox>
|
|
</label>
|
|
<label class="flex flex-col gap-1">
|
|
Zip / 郵遞區號
|
|
<kendo-textbox [(ngModel)]="form.zip"></kendo-textbox>
|
|
</label>
|
|
</div>
|
|
|
|
<label class="flex flex-col gap-1">
|
|
Email / 電郵
|
|
<kendo-textbox [(ngModel)]="form.email"></kendo-textbox>
|
|
</label>
|
|
<label class="flex flex-col gap-1">
|
|
Phone / 電話
|
|
<kendo-textbox [(ngModel)]="form.phone"></kendo-textbox>
|
|
</label>
|
|
|
|
<label class="flex flex-col gap-1">
|
|
W-9 Status / W-9 狀態
|
|
<kendo-dropdownlist [data]="w9Statuses" [(ngModel)]="form.w9Status"></kendo-dropdownlist>
|
|
</label>
|
|
<label class="flex flex-col gap-1">
|
|
W-9 Received / W-9 收到日期
|
|
<kendo-datepicker [(value)]="form.w9ReceivedDate"></kendo-datepicker>
|
|
</label>
|
|
|
|
<!-- W-9 document upload/view: edit mode only (a new record is saved first, then re-opened to attach). -->
|
|
<div *ngIf="editingId != null" class="flex flex-col gap-1 md:col-span-2">
|
|
<span>W-9 Document / W-9 文件</span>
|
|
<input type="file" accept="image/jpeg,image/png,image/webp,application/pdf"
|
|
*appHasPermission="{ module: 'Form1099', action: 'write' }"
|
|
(change)="onW9FileSelected($event)" />
|
|
<span class="hint-text-sm">Upload W-9 / 上傳 W-9</span>
|
|
<button *ngIf="editingHasW9" kendoButton type="button" fillMode="link" class="self-start"
|
|
(click)="viewW9()">View W-9 / 檢視 W-9</button>
|
|
</div>
|
|
|
|
<label class="flex flex-col gap-1 md:col-span-2">
|
|
Notes / 備註
|
|
<kendo-textarea [(ngModel)]="form.notes" [rows]="3"></kendo-textarea>
|
|
</label>
|
|
|
|
<label *ngIf="editingId != null" class="flex items-center gap-2 md:col-span-2">
|
|
<input type="checkbox" [(ngModel)]="form.isActive" /> Active / 啟用
|
|
</label>
|
|
|
|
</div>
|
|
<kendo-dialog-actions>
|
|
<button kendoButton (click)="dialogOpen = false">Cancel / 取消</button>
|
|
<button kendoButton themeColor="primary" [disabled]="!form.legalName" (click)="save()">Save / 儲存</button>
|
|
</kendo-dialog-actions>
|
|
</kendo-dialog>
|
|
|
|
</div>
|