Files
PowderCoatingLogix/src/PowderCoating.Web/Views/Customers/Create.cshtml
T
spouliot a0bdd2b5b4 Sweep all .cshtml files for encoding corruption; add pre-commit guard
Replace all corruption variants with HTML entities across 226 view files:
- 3-char UTF-8-as-Win1252 sequences (ae-corruption)
- Standalone smart/curly quotes that break C# Razor expressions
- Partially re-corrupted variants where the 3rd byte was normalised to ASCII

tools/Fix-Encoding.ps1: re-runnable sweep; uses [char] code points so the
script itself never contains a literal non-ASCII character; supports -DryRun

.githooks/pre-commit: blocks commits containing the ae-corruption byte
signature (xc3xa2xe2x82xac); git core.hooksPath = .githooks so the
hook is repo-committed and active for all future work on this machine.

Build clean; 225 unit tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 21:37:10 -04:00

385 lines
27 KiB
Plaintext

@model PowderCoating.Application.DTOs.Customer.CreateCustomerDto
@{
ViewData["Title"] = "Add New Customer";
ViewData["PageIcon"] = "bi-person-plus";
}
<div class="row justify-content-center">
<div class="col-lg-10">
<div class="d-flex justify-content-end align-items-center mb-4">
<a asp-action="Index" class="btn btn-outline-secondary">
<i class="bi bi-arrow-left me-2"></i>Back to List
</a>
</div>
<div class="card border-0 shadow-sm">
<div class="card-body p-4">
<form asp-action="Create" method="post">
<partial name="_ValidationSummary" />
<!-- Company Information Section -->
<div class="mb-4">
<div class="d-flex align-items-center gap-2 border-bottom pb-2 mb-3">
<h5 class="mb-0"><i class="bi bi-building me-2 text-primary"></i>Company Information</h5>
<a tabindex="0" class="help-icon" role="button"
data-bs-toggle="popover" data-bs-placement="right" data-bs-trigger="focus"
data-bs-title="Company Information"
data-bs-content="Company Name is used on quotes, invoices, and correspondence. Leave it blank for individual (non-business) customers. Customer Type controls which features are available &mdash; Commercial customers get payment terms, credit limits, and pricing tier discounts; Individual customers are for simpler one-off work.">
<i class="bi bi-question-circle"></i>
</a>
</div>
<div class="row g-3">
<div class="col-md-8">
<label asp-for="CompanyName" class="form-label">Company Name</label>
<input asp-for="CompanyName" class="form-control" placeholder="Enter company name" />
<span asp-validation-for="CompanyName" class="text-danger"></span>
</div>
<div class="col-md-4">
<label asp-for="IsCommercial" class="form-label">Customer Type
<a tabindex="0" class="help-icon" role="button"
data-bs-toggle="popover" data-bs-placement="right" data-bs-trigger="focus"
data-bs-title="Customer Type"
data-bs-content="Commercial: businesses with ongoing work, purchase orders, and invoicing. Individual: walk-in customers or one-off jobs. This affects which fields are shown and whether pricing tier discounts apply.">
<i class="bi bi-question-circle"></i>
</a>
</label>
<select asp-for="IsCommercial" class="form-select">
<option value="false">Individual</option>
<option value="true">Commercial</option>
</select>
</div>
</div>
</div>
<!-- Contact Information Section -->
<div class="mb-4">
<h5 class="border-bottom pb-2 mb-3">
<i class="bi bi-person me-2 text-primary"></i>Contact Information
</h5>
<div class="alert alert-info alert-permanent py-2 px-3 mb-3" style="font-size:.875rem;">
<i class="bi bi-info-circle me-1"></i>
<strong>Required:</strong> At least one of Company Name, First Name, or Last Name &mdash; and at least one of Email, Phone, or Mobile Phone.
</div>
<div class="row g-3">
<div class="col-md-6">
<label asp-for="ContactFirstName" class="form-label">First Name <span class="text-muted fw-normal">(required if no company name)</span></label>
<input asp-for="ContactFirstName" class="form-control" placeholder="Enter first name" />
<span asp-validation-for="ContactFirstName" class="text-danger"></span>
</div>
<div class="col-md-6">
<label asp-for="ContactLastName" class="form-label">Last Name</label>
<input asp-for="ContactLastName" class="form-control" placeholder="Enter last name" />
<span asp-validation-for="ContactLastName" class="text-danger"></span>
</div>
<div class="col-md-6">
<label asp-for="Email" class="form-label">Email <span class="text-danger">*</span> <span class="text-muted fw-normal">(required if no phone number)</span></label>
<input asp-for="Email" type="email" multiple class="form-control" placeholder="name@example.com (comma-separate multiple)" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="col-md-3">
<label asp-for="Phone" class="form-label">Phone <span class="text-danger">*</span> <span class="text-muted fw-normal">(required if no email)</span></label>
<input asp-for="Phone" type="tel" class="form-control" placeholder="(555) 123-4567" />
<span asp-validation-for="Phone" class="text-danger"></span>
</div>
<div class="col-md-3">
<label asp-for="MobilePhone" class="form-label">Mobile Phone</label>
<input asp-for="MobilePhone" type="tel" class="form-control" placeholder="(555) 123-4567" />
<span asp-validation-for="MobilePhone" class="text-danger"></span>
</div>
<div class="col-md-6" id="billingEmailRow" style="display:none;">
<label asp-for="BillingEmail" class="form-label">Billing / Accounting Email
<span class="text-muted fw-normal">(invoices sent here)</span>
</label>
<input asp-for="BillingEmail" type="email" multiple class="form-control" placeholder="accounting@company.com (comma-separate multiple)" />
<span asp-validation-for="BillingEmail" class="text-danger"></span>
<div class="form-text">When set, invoices are emailed here instead of the contact email.</div>
</div>
</div>
</div>
<!-- Address Section -->
<div class="mb-4">
<h5 class="border-bottom pb-2 mb-3">
<i class="bi bi-geo-alt me-2 text-primary"></i>Address
</h5>
<div class="row g-3">
<div class="col-12">
<label asp-for="Address" class="form-label">Street Address</label>
<input asp-for="Address" class="form-control" placeholder="Enter street address" />
<span asp-validation-for="Address" class="text-danger"></span>
</div>
<div class="col-md-5">
<label asp-for="City" class="form-label">City</label>
<input asp-for="City" class="form-control" placeholder="Enter city" />
<span asp-validation-for="City" class="text-danger"></span>
</div>
<div class="col-md-3">
<label asp-for="State" class="form-label">State</label>
<input asp-for="State" class="form-control" placeholder="Enter state" />
<span asp-validation-for="State" class="text-danger"></span>
</div>
<div class="col-md-2">
<label asp-for="ZipCode" class="form-label">Zip Code</label>
<input asp-for="ZipCode" class="form-control" placeholder="12345" />
<span asp-validation-for="ZipCode" class="text-danger"></span>
</div>
<div class="col-md-2">
<label asp-for="Country" class="form-label">Country</label>
<select asp-for="Country" class="form-select">
<option value="">-- Select --</option>
<option value="USA">USA</option>
<option value="Canada">Canada</option>
<option value="Mexico">Mexico</option>
<option value="Afghanistan">Afghanistan</option>
<option value="Albania">Albania</option>
<option value="Algeria">Algeria</option>
<option value="Argentina">Argentina</option>
<option value="Australia">Australia</option>
<option value="Austria">Austria</option>
<option value="Bangladesh">Bangladesh</option>
<option value="Belgium">Belgium</option>
<option value="Bolivia">Bolivia</option>
<option value="Brazil">Brazil</option>
<option value="Chile">Chile</option>
<option value="China">China</option>
<option value="Colombia">Colombia</option>
<option value="Costa Rica">Costa Rica</option>
<option value="Croatia">Croatia</option>
<option value="Czech Republic">Czech Republic</option>
<option value="Denmark">Denmark</option>
<option value="Dominican Republic">Dominican Republic</option>
<option value="Ecuador">Ecuador</option>
<option value="Egypt">Egypt</option>
<option value="El Salvador">El Salvador</option>
<option value="Finland">Finland</option>
<option value="France">France</option>
<option value="Germany">Germany</option>
<option value="Ghana">Ghana</option>
<option value="Greece">Greece</option>
<option value="Guatemala">Guatemala</option>
<option value="Honduras">Honduras</option>
<option value="Hungary">Hungary</option>
<option value="India">India</option>
<option value="Indonesia">Indonesia</option>
<option value="Iran">Iran</option>
<option value="Iraq">Iraq</option>
<option value="Ireland">Ireland</option>
<option value="Israel">Israel</option>
<option value="Italy">Italy</option>
<option value="Japan">Japan</option>
<option value="Jordan">Jordan</option>
<option value="Kazakhstan">Kazakhstan</option>
<option value="Kenya">Kenya</option>
<option value="South Korea">South Korea</option>
<option value="Kuwait">Kuwait</option>
<option value="Malaysia">Malaysia</option>
<option value="Netherlands">Netherlands</option>
<option value="New Zealand">New Zealand</option>
<option value="Nicaragua">Nicaragua</option>
<option value="Nigeria">Nigeria</option>
<option value="Norway">Norway</option>
<option value="Pakistan">Pakistan</option>
<option value="Panama">Panama</option>
<option value="Paraguay">Paraguay</option>
<option value="Peru">Peru</option>
<option value="Philippines">Philippines</option>
<option value="Poland">Poland</option>
<option value="Portugal">Portugal</option>
<option value="Puerto Rico">Puerto Rico</option>
<option value="Romania">Romania</option>
<option value="Russia">Russia</option>
<option value="Saudi Arabia">Saudi Arabia</option>
<option value="South Africa">South Africa</option>
<option value="Spain">Spain</option>
<option value="Sweden">Sweden</option>
<option value="Switzerland">Switzerland</option>
<option value="Taiwan">Taiwan</option>
<option value="Thailand">Thailand</option>
<option value="Turkey">Turkey</option>
<option value="Ukraine">Ukraine</option>
<option value="United Arab Emirates">United Arab Emirates</option>
<option value="United Kingdom">United Kingdom</option>
<option value="Uruguay">Uruguay</option>
<option value="Venezuela">Venezuela</option>
<option value="Vietnam">Vietnam</option>
</select>
<span asp-validation-for="Country" class="text-danger"></span>
</div>
</div>
</div>
<!-- Business Information Section -->
<div class="mb-4">
<div class="d-flex align-items-center gap-2 border-bottom pb-2 mb-3">
<h5 class="mb-0"><i class="bi bi-briefcase me-2 text-primary"></i>Business Information</h5>
<a tabindex="0" class="help-icon" role="button"
data-bs-toggle="popover" data-bs-placement="right" data-bs-trigger="focus"
data-bs-title="Business Information"
data-bs-content="These fields govern billing and compliance. Payment Terms sets the default for invoices (e.g., Net 30 = payment due within 30 days). Credit Limit is a soft cap on outstanding balance &mdash; the system will warn when exceeded. Tax Exempt removes tax from all invoices for this customer (upload the exemption certificate on the Edit page).">
<i class="bi bi-question-circle"></i>
</a>
</div>
<div class="row g-3">
<div class="col-md-6">
<label asp-for="TaxId" class="form-label">Tax ID / EIN</label>
<input asp-for="TaxId" class="form-control" placeholder="Enter tax ID" />
<span asp-validation-for="TaxId" class="text-danger"></span>
</div>
<div class="col-md-6">
<div class="d-flex align-items-center gap-1">
<label asp-for="PaymentTerms" class="form-label mb-0">Payment Terms</label>
<a tabindex="0" class="help-icon" role="button"
data-bs-toggle="popover" data-bs-placement="right" data-bs-trigger="focus"
data-bs-title="Payment Terms"
data-bs-content="Sets the default due date on invoices for this customer. 'Net 30' means payment is due 30 days after the invoice date. This is a default &mdash; you can override it on individual invoices.">
<i class="bi bi-question-circle"></i>
</a>
</div>
<select asp-for="PaymentTerms" class="form-select">
<option value="">Select payment terms</option>
<option value="Net 15">Net 15</option>
<option value="Net 30">Net 30</option>
<option value="Net 45">Net 45</option>
<option value="Net 60">Net 60</option>
<option value="Due on Receipt">Due on Receipt</option>
<option value="Cash on Delivery">Cash on Delivery</option>
</select>
<span asp-validation-for="PaymentTerms" class="text-danger"></span>
</div>
<div class="col-md-3">
<label asp-for="PricingTierId" class="form-label">Pricing Tier</label>
<select asp-for="PricingTierId" asp-items="ViewBag.PricingTiers" class="form-select">
<option value="">&mdash; No tier &mdash;</option>
</select>
<small class="text-muted">Applies a discount to all quotes for this customer.</small>
</div>
<div class="col-md-3">
<div class="d-flex align-items-center gap-1">
<label asp-for="CreditLimit" class="form-label mb-0">Credit Limit</label>
<a tabindex="0" class="help-icon" role="button"
data-bs-toggle="popover" data-bs-placement="right" data-bs-trigger="focus"
data-bs-title="Credit Limit"
data-bs-content="The maximum outstanding balance you'll extend to this customer before requiring payment. Set to $0 to allow unlimited credit or to require payment upfront. The system will warn (but not block) when the balance exceeds this limit.">
<i class="bi bi-question-circle"></i>
</a>
</div>
<div class="input-group">
<span class="input-group-text">$</span>
<input asp-for="CreditLimit" type="number" step="0.01" min="0" value="0" class="form-control" placeholder="0.00" />
</div>
<span asp-validation-for="CreditLimit" class="text-danger"></span>
</div>
</div>
<div class="row g-3 mt-2">
<div class="col-md-6">
<div class="form-check form-switch">
<input asp-for="IsTaxExempt" class="form-check-input" type="checkbox" />
<label asp-for="IsTaxExempt" class="form-check-label">Tax Exempt Customer</label>
</div>
<small class="text-muted">Check this box if the customer is tax exempt</small>
</div>
</div>
</div>
<!-- Notes Section -->
<div class="mb-4">
<h5 class="border-bottom pb-2 mb-3">
<i class="bi bi-journal-text me-2 text-primary"></i>Notes
</h5>
<div class="row g-3">
<div class="col-12">
<label asp-for="GeneralNotes" class="form-label">General Notes</label>
<textarea asp-for="GeneralNotes" class="form-control" rows="4" placeholder="Enter any additional notes about this customer"></textarea>
<span asp-validation-for="GeneralNotes" class="text-danger"></span>
</div>
</div>
</div>
<!-- Notification Preferences -->
<div class="mb-4">
<div class="d-flex align-items-center gap-2 border-bottom pb-2 mb-3">
<h5 class="mb-0"><i class="bi bi-bell me-2 text-primary"></i>Notification Preferences</h5>
<a tabindex="0" class="help-icon" role="button"
data-bs-toggle="popover" data-bs-placement="right" data-bs-trigger="focus"
data-bs-title="Notification Preferences"
data-bs-content="Controls when the customer receives automatic updates. Email notifications send status change alerts (e.g., job ready for pickup) to the customer's email address. SMS requires separate TCPA consent and a mobile phone number.">
<i class="bi bi-question-circle"></i>
</a>
</div>
<div class="row g-3">
<!-- Email -->
<div class="col-md-6">
<div class="form-check form-switch">
<input asp-for="NotifyByEmail" class="form-check-input" type="checkbox" role="switch" />
<label asp-for="NotifyByEmail" class="form-check-label">
<i class="bi bi-envelope me-1"></i>Email Notifications
</label>
<div class="form-text">Receive quote and job status updates by email.</div>
</div>
</div>
</div>
@if (ViewBag.SmsEnabled == true)
{
<!-- SMS Consent (TCPA compliance) -->
<div class="mt-3">
<div class="card border-warning">
<div class="card-header bg-warning-subtle text-warning-emphasis d-flex align-items-center gap-2 py-2">
<i class="bi bi-shield-exclamation fs-5"></i>
<span class="fw-semibold">SMS Consent Requirement (TCPA)</span>
<span class="badge bg-warning text-dark ms-auto">Required before enabling SMS</span>
</div>
<div class="card-body pb-2">
<p class="mb-2">
Federal law (TCPA) requires <strong>explicit prior written or verbal consent</strong> before sending SMS messages to a customer.
Before enabling SMS notifications, you must:
</p>
<ol class="mb-2 ps-3">
<li>Inform the customer they will receive automated text messages for job updates and pickup alerts.</li>
<li>Inform them that message and data rates may apply.</li>
<li>Explain they can reply <strong>STOP</strong> at any time to opt out.</li>
<li>Obtain their clear verbal or written agreement.</li>
</ol>
<p class="mb-2 small text-muted">
Only check the box below <strong>after</strong> the customer has given consent.
A confirmation text will be sent automatically to verify enrollment.
</p>
</div>
</div>
<div class="card border-secondary bg-body-secondary p-3">
<div class="form-check">
<input asp-for="SmsConsentGranted" class="form-check-input" type="checkbox" id="SmsConsentGranted" />
<label class="form-check-label fw-semibold" for="SmsConsentGranted">
<i class="bi bi-shield-check me-1 text-success"></i>
Customer has verbally consented to receive SMS notifications
</label>
<div class="form-text">
Checking this box records consent on behalf of the customer and triggers a confirmation text.
A mobile phone number must be entered above.
</div>
</div>
</div>
</div>
}
</div>
<!-- Form Actions -->
<div class="d-flex gap-2 justify-content-end pt-3 border-top">
<a asp-action="Index" class="btn btn-outline-secondary px-4">Cancel</a>
<button type="submit" class="btn btn-primary px-4">
<i class="bi bi-check-circle me-2"></i>Create Customer
</button>
</div>
</form>
</div>
</div>
</div>
</div>
@section Scripts {
<partial name="_ValidationScriptsPartial" />
<script src="~/js/customer-billing-email.js"></script>
}