a0bdd2b5b4
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>
385 lines
27 KiB
Plaintext
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 — 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 — 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 — 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 — 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="">— No tier —</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>
|
|
}
|