Email workspaceInternal accountant pairing request contract
Implementation: App\Mail\internalAccountNotification. Baseline: resources/views/email/accountrequest/internalnotification.blade.php. Usage: app/Http/Controllers/Billing/AppSubscriptionsController.php:170. Static references are evidence, not proof of current production delivery.
Suggested subject: Accountant pairing requested (proposed; current Mailable has no explicit subject). An HTML title does not set the mail header.
Suggested preheader: A business subscription requires virtual accountant pairing.
Placeholder contract
| Token | Existing source / derivation | Handling |
|---|---|---|
| {{logo_url}} | Original Built app_name PNG | Optional deployment override only; concrete public project HTTPS PNG is already present. Approved HTTPS or matching CID. |
| {{preheader}} | Suggested copy above; existing context only | Required escaped text; preserve the existing value. |
| {{plan_name}} | $plan->planname; constructor receives $appbill, not the plan string | Required escaped text; preserve the existing value. |
| {{business_name}} | $business->name | Required escaped text; preserve the existing value. |
| {{business_address}} | $business->address | Optional contact value; use an escaped em dash when absent, retaining the row. |
| {{business_email}} | $business->email | Optional contact value; escaped text, em dash when absent. Not a new recipient or mailto action. |
| {{business_phone}} | $business->phone_number | Optional contact value; escaped text, em dash when absent. |
| {{business_created_at}} | $business->created_at string as in existing view | Required source creation value; do not infer a timezone or substitute subscription creation. |
| {{user_full_name}} | $user->first_name . ' ' . $user->last_name | Required escaped text; preserve the existing value. |
| {{user_email}} | $user->email | Required escaped user contact; not the staff recipient. |
| {{user_phone}} | $user->phone_number | Optional contact value; escaped em dash when absent. |
| {{user_created_at}} | $user->created_at string as in existing view | Required escaped text; preserve the existing value. |
| {{accountant_notice}} | $has_accountant conditional fixed sentence in existing view | Optional escaped fixed text: This business already has an invited accountant on Built, but the accountant was not paired through Built. Remove the entire marked paragraph when false. |
| {{admin_url}} | Existing https://xxxyydd234.builtaccounting.com/business/ + $business->id | Required server-built HTTPS URL on the approved Built super-admin host. Preserve the existing path and record ID, and validate before attribute escaping. |
Content and integration decisions
- Internal-only operations email. Explicitly queued only in production, in the first AccountantRequest creation branch. Keep current To/BCC routing server-side; never send the business/user contact table to the customer as onboarding.
- Preserve all original business/user rows including separate account creation values. The display names are the business and requesting user, not the internal recipient. Optional contact rows use explicit em-dash fallback in the adapter rather than empty cells or fabricated contact data.
- has_accountant is an existing boolean, computed from an accountant invitation. True does not mean Built has paired an accountant. Keep that exact distinction. It controls the full marked notice paragraph; false removes the paragraph and marker. Remove its corresponding plain-text line as well. Never stringify the boolean as the customer-facing notice.
- States: no invited accountant, invited accountant, long contacts/identities/URL, and absent optional contacts. Portable HTML includes the marked optional paragraph; {{accountant_notice}} is fixed escaped prose, never trusted HTML.
- ASAP is expressed as arrange pairing as a priority for staff. This adds no customer-facing pairing deadline or approval. Admin action label corrects the legacy spelling and accurately describes the existing business record destination.
Safe integration
Portable {{placeholder_name}} tokens are not automatically Laravel Blade variables. Prefer an explicitly mapped Blade view within the existing Mailable during later authorized integration; retain the current data, subject/header choices, queue behavior and recipients. No new templating framework is needed. ready/accountant-pairing.html and .txt are the standalone extracts; every fixture has HTML and text alternatives. No existing plain-text source was found for these five Markdown views; the supplied text alternatives are proposed equivalents.
Escape all dynamic text and attribute values. Validate each URL separately against the exact intended HTTPS host/path, then escape it. A contact email is text, not raw markup. Build admin URLs on the server from existing IDs; do not accept arbitrary externally supplied admin destinations. The shared logo defaults to https://built-storage.ams3.cdn.digitaloceanspaces.com/Built/app_name.png; for deployment host that original PNG over absolute HTTPS or attach it with a matching CID and preserve its aspect ratio. Retain assets/built-logo.png as the original source. Remote-image blocking is still possible.
Button and visible fallback share the same URL where a fallback exists; no legacy duplicate URL field is added. Apart from the explicitly marked accountant-pairing notice and optional contact fallback, all documented values are required. No optional block may leak unresolved tokens. Use one-pass replacement only if retaining portable tokens; do not evaluate inserted text or treat it as raw HTML.
Previews use fictional identities, reserved example.com contacts and inert local action explanations. No signup, booking, form submission or mail send happens in this workspace. Native dark-mode styles are progressive enhancement; browser QA does not establish Gmail, Outlook or iOS Mail compatibility.