Email workspaceSales receipt contract
Implementation: App\Mail\salesReciept, app/Mail/salesReciept.php. Baseline view: resources/views/emails/invoice/salesReciept.blade.php, using mail::message. Referenced calls are static usage evidence, not proof of current production delivery.
Suggested subject: Sales receipt #{{sales_number}} (existing exact pattern).
Suggested preheader: Receipt from {{business_name}} for sale {{sales_number}}.
Placeholder contract
| Token | Existing source / derivation | Handling |
|---|---|---|
| {{logo_url}} | Original Built app_name PNG | Optional deployment override; a concrete approved HTTPS logo already exists in ready HTML. Use approved absolute HTTPS or a real matching CID. |
| {{preheader}} | Suggested existing business/reference summary above | Required escaped text; no new payment, settlement or delivery claim. |
| {{business_name}} | $business->name passed/resolved by the existing Mailable | Required escaped merchant identity; not the customer, sender email or owner name. |
| {{sales_number}} | $sale->sales_number | Required escaped source sale reference. Preserve the original spelling/case of the salesReciept Mailable/view. |
| {{receipt_amount}} | $business->currency->symbol . number_format($sale->amount_due, 2) | Required complete existing display, with supplied currency symbol, two decimals, zero and sign. This maps to amount_due despite the old email’s Amount Paid label. Do not replace with amount_due + balance, an inferred transaction amount, amount_paid, FX currency or a newly computed settled value. |
| {{payment_date}} | $sale->entry_date as rendered by the original Blade view | Required escaped source string. Preserve its existing date/time representation; do not borrow the invoice receipt’s Carbon formatting or substitute created_at. |
| {{receipt_url}} | $data[link] = config('app.url') . '/viewreceipt/' . $sale->enc_id | Required complete server-generated HTTPS URL. Preserve the encrypted enc_id value unchanged; do not replace it with the numeric ID or invoice UUID. Button/fallback use the same source link, with separately validated HTTPS host/path and escaped attribute. |
Usage, recipients, values and attachments
- Caller: app/Http/Controllers/General/emailsController.php:515–532 sendreciept validates request.email with required|email:dns, gets current business, finds Sale by business_id + id, returns Sale not found if absent, then explicitly calls Mail::to(request.email)->queue(new salesReciept(sale)). That queue() call is the evidence; Queueable alone does not establish queued delivery. It is a request-selected recipient, not necessarily sale.customer or the business owner. Preserve the business scope and authorization in the consuming app; the email design supplies neither.
- routes/api.php:937 POST sendreciept/{id} and routes/v3.php:98 POST sales/{id}/send-receipt call that controller. The adjacent sendreciept-sms endpoint is a different SMS channel and is not another HTML email.
- Keep the Mailable’s business lookup via sale.business_id, link, sale and loaded SaleItem collection. Preserve DomPDF Pdf::loadView(PDF.salesreceipt, $data), exact Receipt_#sales_number.pdf filename and Sales receipt #sales_number subject. Do not remove PDF-only items, amounts, balance/change, taxes, notes or business fields because the cover does not duplicate them.
- The original body labels business.currency.symbol + sale.amount_due as Amount Paid. PDF.salesreceipt separately labels amount_due as Amount Due and amount_due + balance as Amount Paid, with positive balance shown as change. This is a real source distinction, not permission to fix accounting semantics through HTML. The draft keeps the body expression and uses neutral Receipt amount; financial-label policy or changes to the amount require a separate product/integration decision.
- Keep raw entry_date as Payment date, existing business-name sign-off and attachment instruction. No payment method, customer name, merchant contact block, balance, status badge or new deadline is added because the source cover does not supply them.
- routes/web.php:96 maps /viewreceipt/{id} to salesController::preview (line 888). It decrypts the supplied id, loads sale/business and displays salespreview. Keep the existing encrypted-ID URL; it is a browser receipt page rather than a direct PDF or checkout.
- States: standard, supplied zero and long business/sale reference/amount/date/URL. Zero tests rendering only. No conditional business contact section exists in the source sales cover, so none is added.
From uses config(mail.from.address) with business.name. Reply-To uses business.email ?? business owner.email, with business.name as display name. Preserve null-coalescing semantics (an empty string differs from null), owner lookup and exact source headers. The body’s optional business email is not automatically replaced by the owner’s Reply-To address. No recipient/CC/BCC, queue or attachment change is authorized by this design.
Integration and safe handling
Portable {{placeholder_name}} tokens are not automatically Laravel Blade variables. Prefer the existing Mailable with a complete HTML Blade view and a separate plain-text view using explicit mapped values; do not nest this full document inside mail::message or introduce another templating framework. Keep native header, queue, business authorization, PDF and recipient behavior. Follow docs/integration/laravel.md.
Escape all dynamic text/attributes: identifiers, names, amounts, methods, dates and contacts. Validate the complete receipt URL’s HTTPS scheme, approved application host and exact existing path separately; preserve encrypted/UUID components without decoding/rebuilding in the template. Bind the same receipt_url to the button and visible fallback. There is one source link, not separate legacy duplicate action fields. Browser preview URLs are local inert explanations and must not enter production.
If using one-pass string replacement, escape text first and validate URL values separately, then replace only original tokens once. Inserted token-looking text must remain text, not be evaluated again; no dynamic value is trusted raw HTML. Optional blocks must be controlled explicitly by the integration adapter rather than leaving empty/unresolved tokens. Required missing/invalid amount/date/reference/URL needs an explicit integration decision, not an invented fallback or silently dropped content.
Ready HTML has the approved concrete https://built-storage.ams3.cdn.digitaloceanspaces.com/Built/app_name.png logo with descriptive Built alt text and proportional white-backed rendering. Retain assets/brand/built-logo.png. Deploy on accessible approved absolute HTTPS hosting or attach the original PNG and reference a real matching CID. Preview-relative assets are local fixtures only. Remote-image blocking, rounded corners and native dark styling vary by email client.
Alternatives and open decisions
public/ready/sales-receipt.html and .txt are portable extracts; public/previews/sales-receipt-*.html/.txt contain fictional sample data. No separate project-owned text alternative was found for the Markdown source, so the .txt is a proposed equivalent and must be registered in the consuming Mailable explicitly.
Legacy resources/views/mails/receipt.blade.php and mails/salesreciept.blade.php are separate unresolved-dispatch surfaces with different historical bindings. Do not migrate them merely because their names resemble these referenced Mailables. This batch does not include payment-link receipts, deposit receipts, waybills, SMS or externally authored CRM content.
Open integration decisions: native Blade/text registration, image hosting/CID, client approval, treatment of invalid required source values and explicit review of the documented amount-label semantics. Existing amounts, filenames and routes are preserved. No real recipients, live tokens, emails, payment actions or PDFs are generated here. Browser rendering does not prove Gmail, Outlook, iOS Mail compatibility, PDF correctness or actual payment/delivery status.