feat(invoices): manual invoice creation, work-order conversion, configurable From identities - #49
Merged
Merged
Conversation
…gurable From identities Add explicit invoicedAt domain field so manually-created and converted invoices classify correctly (backward compatible with sent-date/status). Add configurable From identities in Settings + dropdown in EmailComposer. Add convertToInvoice service with per-item and bulk UI.
|
🔥 Firebase Hosting preview: https://open-ten99--pr-49-lbmo2y7i.web.app |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Makes "is this an invoice" explicit and adds the missing pieces around manual invoicing, work-order conversion, and configurable sender identities.
1. Predicate: explicit invoice classification (backward compatible)
WorkItem.invoicedAt?: Date— set when an item becomes an invoice via manual creation or conversion, independent of being emailed.isInvoice = Boolean(invoicedAt) || Boolean(invoiceSentDate) || invoiceStatus ∈ {sent,paid,overdue}.isWorkOrder = !isInvoiceunchanged.invoiceStatus: 'draft', no send) previously leaked into work-order views becausedraftis not an invoice status. They now classify viainvoicedAt.invoicedAt(Timestamp ↔ Date) indocToWorkItem,createWorkItem,updateWorkItem.2. Configurable "From" identities + dropdown
AppSettings.fromIdentities?: FromIdentity[]({ id, name, email, isDefault }).updateSettingspath. Helper text: each address must be a verified sender in Brevo or sending will fail.From:line replaced with a<select>ofsettings.fromIdentities, preselecting the default (else first; else falls back toBRAND.fromEmail/fromNamewhen none configured). Chosen identity is passed asfromEmail/fromNametosendCompletionEmailfor both work-order and invoice emails.saveEmailTemplatefromEmail/fromName), andloadTemplaterestores the selection by matching the saved email to an identity. Template system not rebuilt.3. Manual blank invoice creation
NewInvoiceModal+ route already existed; the modal now stampsinvoicedAt = nowalongsideinvoiceStatus = 'draft'so manually-created invoices appear under Invoices and NOT in work-order lists.4. Convert work order → invoice (per-item + bulk)
services/firestore.ts: newconvertToInvoice(ids)— stampsinvoicedAt = now, setsinvoiceStatus = 'draft'only when unset (preserves existing status), does not touch send fields. Mirrors the bulk-update pattern.isWorkOrder(item).Files changed (by part)
web/src/lib/types.ts,web/src/lib/workItem.ts,web/src/lib/workItem.test.ts,web/src/services/firestore.ts(converter + create/update)web/src/lib/types.ts(FromIdentity, AppSettings),web/src/services/firestore.ts(subscribeSettings),web/src/routes/contractor/Settings.tsx,web/src/routes/contractor/EmailComposer.tsxweb/src/components/finance/NewInvoiceModal.tsxweb/src/services/firestore.ts(convertToInvoice),web/src/routes/contractor/WorkItemDetail.tsx,web/src/routes/contractor/WorkItems.tsxNo new routes were added (the Invoices route + New Invoice modal already existed).
Caveat
From identities must be verified senders in Brevo or the
sendCompletionEmailcall will fail at send time. Surfaced as helper text in both Settings and the composer.Test plan
npx tsc -b— exit 0npx vitest run— 70/70 pass;workItem.test.tsextended to 11 tests coveringinvoicedAt, backward-compat, mutual exclusivity, and convert-to-invoice effectnpm run lint— no NEW errors/warnings in authored code; only pre-existingNewInvoiceModalset-state-in-effect error and firestoreanywarnings remain (present onmain)