Adds a Payee1099 recipient master (encrypted TIN, W-9, optional Member
link), Form1099Box catalog + category->box mapping, a cash-basis year-end
1099 report (per-recipient x box, $600 + missing-W9 flags), recipient
Copy B 1099-NEC PDF + filing CSV, W-9 upload, write-gated TIN reveal, and
a ChurchProfile payer EIN. New Form1099 permission module + admin pages.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Populate the PAYER's TIN (EIN) box in Form1099FormService.BuildCopyBHtml
from ChurchProfile.PayerEin (blank when null, matching prior behaviour).
Add payerEin field to ChurchProfileDto TS model (flows into
UpdateChurchProfileRequest via the existing Omit type) and a text input on
the Church Info tab of the church-profile settings page, mirroring the
Routing # field pattern. CSV left unchanged — adding a payer context line
would break the existing ExportFilingCsvAsync assertion (3 lines expected).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add PayerEin (nullable string, max 20) to ChurchProfile entity, AppDbContext
config, ChurchProfileDto response, UpdateChurchProfileRequest, and
ChurchProfileService round-trip — mirroring the Phone/BankRoutingNumber
nullable-string pattern. Migration AddPayerEinToChurchProfile adds only the
one nullable column to ChurchProfiles.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
Wires the existing GET payee-1099/{id}/tin endpoint into the edit dialog:
a "Reveal full TIN" button guarded by *appHasPermission Form1099:write that
fetches and displays the decrypted TIN read-only. Satisfies acceptance
criterion #11.4. The value is never logged or persisted.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds POST/GET payee-1099/{id}/w9, mirroring the expense-receipt upload:
IFileStorage saves to finance/w9/{id}{ext}, content-type derived from the
blob extension. Frontend dialog (edit mode) gains a W-9 file input and an
auth-correct blob "View W-9" link. Payee1099Service ctor now takes
IFileStorage; tests updated with an in-memory FakeStorage.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- DB_SCHEMA.md §8: add Form1099Box catalog table, Payee1099 recipient master
(with TIN at-rest encryption note), and new FK columns on Expenses /
ExpenseSubCategories / ExpenseCategoryGroups; update TOC and Seed Data section
- DbSeeder.cs: grant Modules.Form1099 to finance (R/W/D), pastor (R), and
board_member (R), mirroring the Form990Report + Disbursements pattern;
idempotent (only inserts if row absent, never clobbers admin edits)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
Mirror the 990-line dropdown in both the group and subcategory edit
dialogs: add form1099BoxId to the frontend group/subcategory DTOs and
request interfaces, load boxes via a new getForm1099Boxes() method on
ExpenseCategoryApiService (same label pattern as getForm990Lines:
"boxCode — name_en / name_zh"), wire form1099BoxId into all
open/edit/save paths, and render a side-by-side "1099 Box / 1099 框"
Kendo DropdownList with [valuePrimitive]="true" and "— none —" default.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mirror Form990LineId: add Form1099BoxId + Form1099BoxCode to all four
category DTOs (response + request, group + sub); load a boxCodes lookup
dictionary in GetAllAsync and project it; set/copy the field in
CreateGroupAsync, UpdateGroupAsync, CreateSubCategoryAsync, and
UpdateSubCategoryAsync. All 4 category-service unit tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add Form1099ReportPageComponent (year selector, summary chips with a
prominent missing-W-9 flag, desktop grid + mobile cards, recipient detail
dialog). Per-row Copy B PDF via right-click context menu and a header
Export filing CSV action, both downloaded as auth-correct blobs. Wire the
eager route + sidebar nav item, gated on Form1099:read. Also convert the
neighboring finance/payee-1099 route from lazy loadComponent to an eager
component import so both 1099 routes match the surrounding convention.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add downloadCsv/downloadCopyB returning Blob via HttpClient so the auth
interceptor attaches the bearer token (raw window.open would 401). Remove
the now-unused copyBUrl/exportCsvUrl raw-URL builders.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Injects I1099FormService into Form1099ReportController and adds two
Read-gated GET endpoints: recipient/{payeeId}/copy-b (Copy B PDF) and
export-csv (filing-data CSV). Registers Form1099FormService in DI.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds I1099FormService and Form1099FormService: an IRIS/accountant filing-data
CSV (one row per reportable recipient) and a plain-paper recipient Copy B
1099-NEC PDF rendered via the DevExpress RichEdit/Office API (mirroring
CheckPrintService). Includes a CSV-export unit test over a stub report service.
Service lives in namespace ROLAC.API.Services (not ...Services.Form1099) to
avoid shadowing the ROLAC.API.Entities.Form1099 constants class.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Creates Form1099Boxes and Payee1099s tables; adds Form1099BoxId to
ExpenseSubCategories and ExpenseCategoryGroups; adds PayeeId to
Expenses. All new columns nullable, all FKs with SetNull, unique index
on Form1099Boxes.BoxCode. No data backfill.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds Form1099BoxSeed (NEC-1, MISC-1) and Form1099SubMappingSeed
(6 service/rent subcategories), SeedForm1099BoxesAsync method with
null-fill idempotency (never clobbers admin edits), and wires it into
SeedAsync after SeedForm990ExpenseLinesAsync.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add int? PayeeId to CreateExpenseRequest (UpdateExpenseRequest inherits)
and to ExpenseListItemDto (so it round-trips to the form). Set e.PayeeId
unconditionally in CreateAsync and UpdateAsync so 1099 attribution is
independent of VendorPayment vs StaffReimbursement type. Map PayeeId in
both DTO projections: the paged-list lambda and GetByIdAsync.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add Form1099 const to Modules.cs (after Form990Report) and insert it
into the All display-order list. Register IForm1099ReportService and
IPayee1099Service in Program.cs beside the existing Form990Report entry.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Implement IPayee1099Service and Payee1099Service: list/get/create/update/
soft-delete and RevealTin. TIN is encrypted via ITinProtector on write;
TinLast4 is the only clear-text fragment stored. Null Tin on update
preserves the existing ciphertext. Four xUnit tests cover encrypt-on-create,
null-tin-keeps-ciphertext, list-masks-to-last4, and soft-delete hides from list.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add Payee1099ListItemDto, Payee1099Dto, and SavePayee1099Request in
DTOs/Payee for the 1099 recipient CRUD surface.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Save a vendor payment as a reusable named snapshot and re-apply it later,
pre-filling every field except the Expense Date. Shared church-wide with a
creator tag; quick picker in the vendor form + a management page (rename/delete).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The snapshot management page backs an API that gates every action on
Expenses:Write, so a read-only user reaching it via a read-gated nav/route
would hit a silent 403 and a blank page. Require write for both.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>