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>
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>
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>
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>
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>
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 an AI assist button to the Edit/New Group (大項) and Subcategory
(小項) dialogs: the user enters a Chinese name, and the model refines
the Chinese, translates it to English, and suggests the matching IRS
Form 990 Part IX line. Suggestions surface in a confirm card; Apply
fills the Chinese name, English name, and 990 line fields.
Backend mirrors the existing expense-classification AI family but over
the Form 990 line catalog: IExpenseCategoryAiService + base (catalog
load, prompt, id validation) + Claude/Gemini providers + factory that
picks the provider from ChurchProfile.AiProvider. New write-gated
POST api/expense-categories/ai-suggest endpoint; sub-category requests
pass the parent group + its 990 line to bias the choice.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
TDD: IEmailService interface, EmailService resolves member emails + raw addresses (case-insensitive dedup), sends via ISmtpDispatcher, writes a NotificationLog per recipient (sent/failed), and never aborts the batch on a single failure.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Implements LineSignature.IsValid() using HMAC-SHA256 + FixedTimeEquals to prevent timing attacks; includes xUnit tests for valid, tampered, and null/empty header cases.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>