Add CRM features: Additional Contacts, Lead Source, Ship-To Address; update Help docs
- New CustomerContact entity + migration (AddCustomerContactsAndCrmFields) - Customer.LeadSource + ShipToAddress/City/State/ZipCode/Country fields - Additional Contacts card on Customer Details with AJAX add/edit/delete - Lead Source dropdown on Create/Edit; Ship-To section on Create/Edit - Customer Details: side-by-side billing/ship-to when ship-to is set - Help docs: Customers (contacts, ship-to, lead source, preferred powders, outstanding pickups) - Help docs: Jobs (clone job, project name), Quotes (project name), Invoices (project name), Inventory (low stock clickable filter) - HelpKnowledgeBase.cs updated for all features above Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -209,6 +209,42 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ship-To Address 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-truck me-2 text-primary"></i>Ship-To / Pickup Address</h5>
|
||||
<a tabindex="0" class="help-icon" role="button"
|
||||
data-bs-toggle="popover" data-bs-placement="right" data-bs-trigger="focus"
|
||||
data-bs-title="Ship-To Address"
|
||||
data-bs-content="Optional. Fill in only if this customer picks up or receives deliveries at a different address than their billing address. Leave blank to use the billing address above.">
|
||||
<i class="bi bi-question-circle"></i>
|
||||
</a>
|
||||
<span class="text-muted small fw-normal">(optional — leave blank if same as billing)</span>
|
||||
</div>
|
||||
<div class="row g-3">
|
||||
<div class="col-12">
|
||||
<label asp-for="ShipToAddress" class="form-label">Street Address</label>
|
||||
<input asp-for="ShipToAddress" class="form-control" placeholder="Enter ship-to street address" />
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<label asp-for="ShipToCity" class="form-label">City</label>
|
||||
<input asp-for="ShipToCity" class="form-control" placeholder="Enter city" />
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label asp-for="ShipToState" class="form-label">State</label>
|
||||
<input asp-for="ShipToState" class="form-control" placeholder="Enter state" />
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label asp-for="ShipToZipCode" class="form-label">Zip Code</label>
|
||||
<input asp-for="ShipToZipCode" class="form-control" placeholder="12345" />
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label asp-for="ShipToCountry" class="form-label">Country</label>
|
||||
<input asp-for="ShipToCountry" class="form-control" placeholder="USA" />
|
||||
</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">
|
||||
@@ -282,6 +318,30 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lead Source Section -->
|
||||
<div class="mb-4">
|
||||
<h5 class="border-bottom pb-2 mb-3">
|
||||
<i class="bi bi-signpost me-2 text-primary"></i>How Did They Find Us?
|
||||
</h5>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label asp-for="LeadSource" class="form-label">Lead Source</label>
|
||||
<select asp-for="LeadSource" class="form-select">
|
||||
<option value="">— Not specified —</option>
|
||||
<option value="Walk-In">Walk-In</option>
|
||||
<option value="Google Search">Google Search</option>
|
||||
<option value="Customer Referral">Customer Referral</option>
|
||||
<option value="Social Media">Social Media</option>
|
||||
<option value="Website">Website</option>
|
||||
<option value="Repeat Customer">Repeat Customer</option>
|
||||
<option value="Trade Show / Event">Trade Show / Event</option>
|
||||
<option value="Flyer / Print Ad">Flyer / Print Ad</option>
|
||||
<option value="Other">Other</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Notes Section -->
|
||||
<div class="mb-4">
|
||||
<h5 class="border-bottom pb-2 mb-3">
|
||||
|
||||
@@ -216,27 +216,50 @@
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if (!string.IsNullOrEmpty(Model.Address))
|
||||
@{
|
||||
bool hasBilling = !string.IsNullOrEmpty(Model.Address);
|
||||
bool hasShipTo = !string.IsNullOrEmpty(Model.ShipToAddress) || !string.IsNullOrEmpty(Model.ShipToCity);
|
||||
}
|
||||
@if (hasShipTo)
|
||||
{
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="text-muted small mb-1">Billing Address</label>
|
||||
@if (hasBilling)
|
||||
{
|
||||
<p class="mb-1">@Model.Address</p>
|
||||
<p class="mb-0">
|
||||
@if (!string.IsNullOrEmpty(Model.City)) { <span>@Model.City</span> }
|
||||
@if (!string.IsNullOrEmpty(Model.State)) { <span>, @Model.State</span> }
|
||||
@if (!string.IsNullOrEmpty(Model.ZipCode)) { <span> @Model.ZipCode</span> }
|
||||
</p>
|
||||
@if (!string.IsNullOrEmpty(Model.Country)) { <p class="mb-0 text-muted">@Model.Country</p> }
|
||||
}
|
||||
else { <p class="text-muted mb-0">Not provided</p> }
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="text-muted small mb-1">
|
||||
<i class="bi bi-truck me-1"></i>Ship-To / Pickup Address
|
||||
</label>
|
||||
<p class="mb-1">@Model.ShipToAddress</p>
|
||||
<p class="mb-0">
|
||||
@if (!string.IsNullOrEmpty(Model.ShipToCity)) { <span>@Model.ShipToCity</span> }
|
||||
@if (!string.IsNullOrEmpty(Model.ShipToState)) { <span>, @Model.ShipToState</span> }
|
||||
@if (!string.IsNullOrEmpty(Model.ShipToZipCode)) { <span> @Model.ShipToZipCode</span> }
|
||||
</p>
|
||||
@if (!string.IsNullOrEmpty(Model.ShipToCountry)) { <p class="mb-0 text-muted">@Model.ShipToCountry</p> }
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else if (hasBilling)
|
||||
{
|
||||
<p class="mb-2">@Model.Address</p>
|
||||
<p class="mb-0">
|
||||
@if (!string.IsNullOrEmpty(Model.City))
|
||||
{
|
||||
<span>@Model.City</span>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.State))
|
||||
{
|
||||
<span>, @Model.State</span>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.ZipCode))
|
||||
{
|
||||
<span> @Model.ZipCode</span>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.City)) { <span>@Model.City</span> }
|
||||
@if (!string.IsNullOrEmpty(Model.State)) { <span>, @Model.State</span> }
|
||||
@if (!string.IsNullOrEmpty(Model.ZipCode)) { <span> @Model.ZipCode</span> }
|
||||
</p>
|
||||
@if (!string.IsNullOrEmpty(Model.Country))
|
||||
{
|
||||
<p class="mb-0 text-muted">@Model.Country</p>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.Country)) { <p class="mb-0 text-muted">@Model.Country</p> }
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -262,6 +285,15 @@
|
||||
<label class="text-muted small mb-1">Payment Terms</label>
|
||||
<p class="mb-0">@(Model.PaymentTerms ?? "Not set")</p>
|
||||
</div>
|
||||
@if (!string.IsNullOrEmpty(Model.LeadSource))
|
||||
{
|
||||
<div class="col-md-6">
|
||||
<label class="text-muted small mb-1">Lead Source</label>
|
||||
<p class="mb-0">
|
||||
<i class="bi bi-signpost me-1 text-muted"></i>@Model.LeadSource
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
<div class="col-md-6">
|
||||
<label class="text-muted small mb-1">Credit Limit</label>
|
||||
<p class="mb-0 fw-semibold">@Model.CreditLimit.ToString("C")</p>
|
||||
@@ -329,6 +361,96 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Additional Contacts -->
|
||||
@{
|
||||
var customerContacts = ViewBag.CustomerContacts as List<PowderCoating.Core.Entities.CustomerContact>;
|
||||
}
|
||||
<div class="card border-0 shadow-sm mb-4">
|
||||
<div class="card-header bg-white border-0 py-3 d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0 fw-semibold">
|
||||
<i class="bi bi-people me-2 text-primary"></i>Additional Contacts
|
||||
</h5>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span class="text-muted small">
|
||||
<i class="bi bi-info-circle me-1"></i>For staff reference — automated notifications still go to the primary contact above.
|
||||
</span>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary"
|
||||
data-bs-toggle="modal" data-bs-target="#contactModal"
|
||||
onclick="openAddContactModal()">
|
||||
<i class="bi bi-plus-circle me-1"></i>Add Contact
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-hover mb-0">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th class="ps-3">Name / Role</th>
|
||||
<th>Email</th>
|
||||
<th>Phone</th>
|
||||
<th class="text-end pe-3"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="contacts-table-body">
|
||||
@if (customerContacts != null && customerContacts.Count > 0)
|
||||
{
|
||||
@foreach (var c in customerContacts)
|
||||
{
|
||||
var displayName = string.IsNullOrWhiteSpace(c.LastName) ? c.FirstName : $"{c.FirstName} {c.LastName}";
|
||||
<tr data-contact-id="@c.Id">
|
||||
<td class="ps-3">
|
||||
<div class="fw-semibold">
|
||||
@displayName
|
||||
@if (!string.IsNullOrEmpty(c.ContactRole))
|
||||
{
|
||||
<span class="badge bg-secondary bg-opacity-10 text-secondary ms-1">@c.ContactRole</span>
|
||||
}
|
||||
</div>
|
||||
@if (!string.IsNullOrEmpty(c.Title))
|
||||
{
|
||||
<div class="text-muted" style="font-size:0.75rem;">@c.Title</div>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@if (!string.IsNullOrEmpty(c.Email))
|
||||
{
|
||||
<a href="mailto:@c.Email" class="text-decoration-none small">@c.Email</a>
|
||||
}
|
||||
else { <span class="text-muted small">—</span> }
|
||||
</td>
|
||||
<td>
|
||||
@if (!string.IsNullOrEmpty(c.Phone ?? c.MobilePhone))
|
||||
{
|
||||
<span class="small">@(c.Phone ?? c.MobilePhone)</span>
|
||||
}
|
||||
else { <span class="text-muted small">—</span> }
|
||||
</td>
|
||||
<td class="text-end pe-3">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary me-1"
|
||||
onclick="editContact(@Model.Id, @c.Id)" title="Edit">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger"
|
||||
onclick="deleteContact(@Model.Id, @c.Id)" title="Delete">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<tr id="no-contacts-placeholder">
|
||||
<td colspan="4" class="text-muted small px-3 py-2">No additional contacts. Click “Add Contact” to add billing, ops, or drop-off contacts.</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Customer Notes -->
|
||||
@{
|
||||
var customerNotes = ViewBag.CustomerNotes as List<PowderCoating.Core.Entities.CustomerNote>;
|
||||
@@ -776,6 +898,72 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add / Edit Contact Modal -->
|
||||
<div class="modal fade" id="contactModal" tabindex="-1" aria-labelledby="contactModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="contactModalLabel">
|
||||
<i class="bi bi-person-plus me-2 text-primary"></i><span id="contactModalTitle">Add Contact</span>
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<input type="hidden" id="contactId" value="0" />
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-semibold">First Name <span class="text-danger">*</span></label>
|
||||
<input type="text" id="contactFirstName" class="form-control" maxlength="100" placeholder="First name" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Last Name</label>
|
||||
<input type="text" id="contactLastName" class="form-control" maxlength="100" placeholder="Last name" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Job Title</label>
|
||||
<input type="text" id="contactTitle" class="form-control" maxlength="100" placeholder="e.g. Purchasing Manager" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Role</label>
|
||||
<select id="contactRole" class="form-select">
|
||||
<option value="">— Select —</option>
|
||||
<option value="Billing">Billing</option>
|
||||
<option value="Operations">Operations</option>
|
||||
<option value="Drop-Off">Drop-Off</option>
|
||||
<option value="Sales">Sales</option>
|
||||
<option value="General">General</option>
|
||||
<option value="Other">Other</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label class="form-label">Email</label>
|
||||
<input type="email" id="contactEmail" class="form-control" maxlength="200" placeholder="email@example.com" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Phone</label>
|
||||
<input type="tel" id="contactPhone" class="form-control" maxlength="20" placeholder="(555) 123-4567" />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Mobile Phone</label>
|
||||
<input type="tel" id="contactMobilePhone" class="form-control" maxlength="20" placeholder="(555) 123-4567" />
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label class="form-label">Notes</label>
|
||||
<textarea id="contactNotes" class="form-control" rows="2" maxlength="500" placeholder="Optional notes about this contact..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div id="contactModalError" class="alert alert-danger alert-permanent mt-3 d-none"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-primary" onclick="saveContact(@Model.Id)" id="saveContactBtn">
|
||||
<i class="bi bi-check-circle me-1"></i>Save Contact
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add Store Credit Modal -->
|
||||
@if (User.IsInRole("SuperAdmin") || User.IsInRole("Administrator"))
|
||||
{
|
||||
|
||||
@@ -213,6 +213,42 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ship-To Address 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-truck me-2 text-primary"></i>Ship-To / Pickup Address</h5>
|
||||
<a tabindex="0" class="help-icon" role="button"
|
||||
data-bs-toggle="popover" data-bs-placement="right" data-bs-trigger="focus"
|
||||
data-bs-title="Ship-To Address"
|
||||
data-bs-content="Optional. Fill in only if this customer picks up or receives deliveries at a different address than their billing address. Leave blank to use the billing address above.">
|
||||
<i class="bi bi-question-circle"></i>
|
||||
</a>
|
||||
<span class="text-muted small fw-normal">(optional — leave blank if same as billing)</span>
|
||||
</div>
|
||||
<div class="row g-3">
|
||||
<div class="col-12">
|
||||
<label asp-for="ShipToAddress" class="form-label">Street Address</label>
|
||||
<input asp-for="ShipToAddress" class="form-control" placeholder="Enter ship-to street address" />
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<label asp-for="ShipToCity" class="form-label">City</label>
|
||||
<input asp-for="ShipToCity" class="form-control" placeholder="Enter city" />
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label asp-for="ShipToState" class="form-label">State</label>
|
||||
<input asp-for="ShipToState" class="form-control" placeholder="Enter state" />
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label asp-for="ShipToZipCode" class="form-label">Zip Code</label>
|
||||
<input asp-for="ShipToZipCode" class="form-control" placeholder="12345" />
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label asp-for="ShipToCountry" class="form-label">Country</label>
|
||||
<input asp-for="ShipToCountry" class="form-control" placeholder="USA" />
|
||||
</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">
|
||||
@@ -270,6 +306,30 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lead Source Section -->
|
||||
<div class="mb-4">
|
||||
<h5 class="border-bottom pb-2 mb-3">
|
||||
<i class="bi bi-signpost me-2 text-primary"></i>How Did They Find Us?
|
||||
</h5>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label asp-for="LeadSource" class="form-label">Lead Source</label>
|
||||
<select asp-for="LeadSource" class="form-select">
|
||||
<option value="">— Not specified —</option>
|
||||
<option value="Walk-In">Walk-In</option>
|
||||
<option value="Google Search">Google Search</option>
|
||||
<option value="Customer Referral">Customer Referral</option>
|
||||
<option value="Social Media">Social Media</option>
|
||||
<option value="Website">Website</option>
|
||||
<option value="Repeat Customer">Repeat Customer</option>
|
||||
<option value="Trade Show / Event">Trade Show / Event</option>
|
||||
<option value="Flyer / Print Ad">Flyer / Print Ad</option>
|
||||
<option value="Other">Other</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Notes Section -->
|
||||
<div class="mb-4">
|
||||
<h5 class="border-bottom pb-2 mb-3">
|
||||
|
||||
@@ -136,8 +136,26 @@
|
||||
</p>
|
||||
<p>The details page shows:</p>
|
||||
<ul>
|
||||
<li><strong>Contact information</strong> — name, email, phone, and address.</li>
|
||||
<li><strong>Account summary</strong> — current balance, credit limit, and pricing tier.</li>
|
||||
<li><strong>Contact information</strong> — name, email, phone, address, and lead source.</li>
|
||||
<li><strong>Account summary</strong> — current balance, credit limit, store credit, and pricing tier.</li>
|
||||
<li>
|
||||
<strong>Ready for Pickup</strong> — if any of this customer’s jobs are in “Ready for Pickup” status,
|
||||
a highlighted card appears in the right column showing each job number and how many days it has been waiting.
|
||||
Jobs waiting 3–6 days show in amber; 7+ days in red.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Additional Contacts</strong> — billing contacts, ops contacts, drop-off contacts, and so on.
|
||||
See the Additional Contacts section below.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Internal Notes</strong> — private notes added by your staff (not visible to the customer).
|
||||
Notes can be marked as important <span class="text-warning">★</span> to highlight them for the team.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Preferred Powders</strong> — inventory items this customer frequently uses. Staff can
|
||||
search and add powders here so that anyone creating a quote or job for this customer can quickly
|
||||
see which colors they prefer. See the Preferred Powders section below.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Jobs tab</strong> — every job created for this customer, with status and date. Click
|
||||
a job number to open it.
|
||||
@@ -153,7 +171,7 @@
|
||||
<li>
|
||||
<strong>Deposits tab</strong> — all deposits recorded for this customer across any job or quote.
|
||||
</li>
|
||||
<li><strong>Notes</strong> — any notes saved against the customer record.</li>
|
||||
<li><strong>Recent Activity</strong> — a combined timeline of the last 15 events (jobs, quotes, invoices, deposits) in reverse chronological order.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
@@ -195,6 +213,122 @@
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section id="additional-contacts" class="mb-5">
|
||||
<h2 class="h4 fw-bold border-bottom pb-2 mb-3">
|
||||
<i class="bi bi-people text-primary me-2"></i>Additional Contacts
|
||||
</h2>
|
||||
<p>
|
||||
Commercial customers often have more than one person involved in their account — a purchasing
|
||||
manager, a billing contact, or the person who actually drops off and picks up parts. The
|
||||
<strong>Additional Contacts</strong> section on the Customer Details page lets you store all of
|
||||
them in one place so your team always knows who to call.
|
||||
</p>
|
||||
<p>To add a contact, open the Customer Details page and click <strong>Add Contact</strong> in the
|
||||
Additional Contacts card. You can record:</p>
|
||||
<ul class="mb-3">
|
||||
<li><strong>Name</strong> — first and last name.</li>
|
||||
<li><strong>Job Title</strong> — their role at the company (e.g., “Purchasing Manager”).</li>
|
||||
<li><strong>Role</strong> — a category tag: Billing, Operations, Drop-Off, Sales, General, or Other.</li>
|
||||
<li><strong>Email & Phone</strong> — their direct contact details.</li>
|
||||
<li><strong>Notes</strong> — anything else your team should know about this person.</li>
|
||||
</ul>
|
||||
<div class="alert alert-permanent alert-warning d-flex gap-2 mb-0" role="alert">
|
||||
<i class="bi bi-exclamation-triangle-fill flex-shrink-0 mt-1"></i>
|
||||
<div>
|
||||
<strong>Notifications always go to the primary contact.</strong> Additional contacts are for
|
||||
staff reference only. All automated emails (job ready for pickup, invoice sent, quote
|
||||
approval links, etc.) and SMS messages are sent to the email address and phone number on the
|
||||
main customer record — not to the contacts listed here. If you need invoices routed to a
|
||||
different address, use the <strong>Billing / Accounting Email</strong> field on the main
|
||||
customer record instead.
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="ship-to-address" class="mb-5">
|
||||
<h2 class="h4 fw-bold border-bottom pb-2 mb-3">
|
||||
<i class="bi bi-truck text-primary me-2"></i>Ship-To / Pickup Address
|
||||
</h2>
|
||||
<p>
|
||||
Some customers have a different address for pickups or deliveries than their billing address. You
|
||||
can record a separate <strong>Ship-To</strong> address on the Create or Edit form. Leave it blank
|
||||
if the customer picks up from the same address they bill from.
|
||||
</p>
|
||||
<p>
|
||||
When a ship-to address is on file, the Customer Details page splits the Address card into two
|
||||
columns — billing on the left, ship-to on the right — so the difference is immediately visible
|
||||
to anyone looking up the customer.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section id="lead-source" class="mb-5">
|
||||
<h2 class="h4 fw-bold border-bottom pb-2 mb-3">
|
||||
<i class="bi bi-signpost text-primary me-2"></i>Lead Source
|
||||
</h2>
|
||||
<p>
|
||||
The <strong>Lead Source</strong> field lets you record how a customer found your shop. Options
|
||||
include Walk-In, Google Search, Customer Referral, Social Media, Website, Repeat Customer, Trade
|
||||
Show / Event, Flyer / Print Ad, and Other.
|
||||
</p>
|
||||
<p>
|
||||
This field is optional and is shown on the Customer Details page under Business Information. It
|
||||
is useful for understanding which marketing channels are bringing in customers over time.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section id="preferred-powders" class="mb-5">
|
||||
<h2 class="h4 fw-bold border-bottom pb-2 mb-3">
|
||||
<i class="bi bi-droplet-half text-primary me-2"></i>Preferred Powders
|
||||
</h2>
|
||||
<p>
|
||||
The <strong>Preferred Powders</strong> card on the Customer Details page lets you tag inventory
|
||||
items that this customer regularly orders. It is a staff-reference tool — it does not auto-select
|
||||
powders on quotes or jobs, but it gives anyone creating a quote a quick look at what colors this
|
||||
customer has used before.
|
||||
</p>
|
||||
<p>To add a preferred powder:</p>
|
||||
<ol class="mb-3">
|
||||
<li class="mb-1">Open the Customer Details page.</li>
|
||||
<li class="mb-1">In the <strong>Preferred Powders</strong> card, type part of the powder name or SKU into the search box.</li>
|
||||
<li class="mb-1">Select the item from the dropdown and click <strong>Add</strong>.</li>
|
||||
</ol>
|
||||
<p>To remove a preferred powder, click the <strong>×</strong> button next to the item in the list.</p>
|
||||
<div class="alert alert-permanent alert-info d-flex gap-2 mb-0" role="alert">
|
||||
<i class="bi bi-info-circle-fill flex-shrink-0 mt-1"></i>
|
||||
<div>
|
||||
Only items that already exist in your <strong>Inventory</strong> can be added as preferred powders.
|
||||
If a color isn’t appearing in the search, check that it has been added to inventory first.
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="outstanding-pickups" class="mb-5">
|
||||
<h2 class="h4 fw-bold border-bottom pb-2 mb-3">
|
||||
<i class="bi bi-box-seam text-primary me-2"></i>Outstanding Pickups
|
||||
</h2>
|
||||
<p>
|
||||
When one or more of a customer’s jobs are in <strong>Ready for Pickup</strong> status, a
|
||||
highlighted card appears in the right column of their Customer Details page. This lets your front desk
|
||||
staff immediately see — without opening the Jobs list — whether a customer calling or walking
|
||||
in has finished work waiting for them.
|
||||
</p>
|
||||
<p>The card shows:</p>
|
||||
<ul class="mb-3">
|
||||
<li>The job number (clickable, opens the Job Details page).</li>
|
||||
<li>How many days the job has been waiting in “Ready for Pickup” status.</li>
|
||||
</ul>
|
||||
<p>Color coding helps prioritize follow-up calls:</p>
|
||||
<ul class="mb-3">
|
||||
<li><span class="badge bg-warning text-dark">Amber</span> — waiting 3–6 days.</li>
|
||||
<li><span class="badge bg-danger">Red</span> — waiting 7 or more days.</li>
|
||||
<li>No color — waiting 0–2 days (recently completed).</li>
|
||||
</ul>
|
||||
<p>
|
||||
The card disappears automatically once all jobs for this customer have moved out of
|
||||
“Ready for Pickup” status (e.g., to Delivered).
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section id="deactivating-a-customer" class="mb-5">
|
||||
<h2 class="h4 fw-bold border-bottom pb-2 mb-3">
|
||||
<i class="bi bi-person-dash text-primary me-2"></i>Deactivating a Customer
|
||||
@@ -235,6 +369,11 @@
|
||||
<a class="nav-link py-1 px-3 small text-body" href="#customer-details">Customer Details Page</a>
|
||||
<a class="nav-link py-1 px-3 small text-body" href="#credit-limit">Credit Limit</a>
|
||||
<a class="nav-link py-1 px-3 small text-body" href="#tax-exempt">Tax Exempt</a>
|
||||
<a class="nav-link py-1 px-3 small text-body" href="#additional-contacts">Additional Contacts</a>
|
||||
<a class="nav-link py-1 px-3 small text-body" href="#ship-to-address">Ship-To Address</a>
|
||||
<a class="nav-link py-1 px-3 small text-body" href="#lead-source">Lead Source</a>
|
||||
<a class="nav-link py-1 px-3 small text-body" href="#preferred-powders">Preferred Powders</a>
|
||||
<a class="nav-link py-1 px-3 small text-body" href="#outstanding-pickups">Outstanding Pickups</a>
|
||||
<a class="nav-link py-1 px-3 small text-body" href="#deactivating-a-customer">Deactivating a Customer</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
@@ -415,7 +415,11 @@
|
||||
An alert banner is shown on the item's Details page prompting you to use Stock Adjustment to add inventory.
|
||||
</li>
|
||||
</ul>
|
||||
<p>Low Stock and Out of Stock items appear in the Inventory Alerts section on the Dashboard and in the Operations Report. Use the <strong>Low Stock</strong> filter on the Inventory list to see only items needing attention.</p>
|
||||
<p>
|
||||
Low Stock and Out of Stock items appear in the Inventory Alerts section on the Dashboard and in the Operations Report.
|
||||
The <strong>Low Stock</strong> stat card at the top of the Inventory page is clickable — click it to instantly
|
||||
filter the list to only items needing attention. Click it again (or clear the filter) to return to the full list.
|
||||
</p>
|
||||
<div class="alert alert-permanent alert-warning d-flex gap-2 mb-0" role="alert">
|
||||
<i class="bi bi-exclamation-triangle-fill flex-shrink-0 mt-1"></i>
|
||||
<div>
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
<li class="mb-2">Select the customer and then select the job this invoice is for.</li>
|
||||
<li class="mb-2">Add or adjust line items as needed.</li>
|
||||
<li class="mb-2">Set the invoice date, due date, and any notes.</li>
|
||||
<li class="mb-2">Optionally enter a <strong>Project Name</strong>. When creating from a job, this pre-fills from the job's project name automatically.</li>
|
||||
<li class="mb-2">Click <strong>Save Invoice</strong>.</li>
|
||||
</ol>
|
||||
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
<li class="mb-2">Choose a <strong>Priority</strong> — Normal is the default; see the Job Priority section below for all levels.</li>
|
||||
<li class="mb-2">Optionally assign a <strong>Worker</strong> from your shop workers list.</li>
|
||||
<li class="mb-2">Enter the customer's <strong>PO Number</strong> if they require one for their own records.</li>
|
||||
<li class="mb-2">Optionally enter a <strong>Project Name</strong> — a short label that groups related jobs (e.g., “Spring Fleet Refresh”). It appears on the job, its invoice, and printed work orders.</li>
|
||||
<li class="mb-2">Add any <strong>Special Instructions</strong> your team needs to know before starting work.</li>
|
||||
<li class="mb-2">Add one or more <strong>Line Items</strong> describing each piece being coated. See the Job Items section below.</li>
|
||||
<li class="mb-2">
|
||||
@@ -553,6 +554,33 @@
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<section id="clone-job" class="mb-5">
|
||||
<h2 class="h4 fw-bold border-bottom pb-2 mb-3">
|
||||
<i class="bi bi-copy text-primary me-2"></i>Cloning a Job
|
||||
</h2>
|
||||
<p>
|
||||
If you need to create a new job that is identical or very similar to one you have already done,
|
||||
use the <strong>Clone</strong> button on the Job Details page. This saves you from re-entering
|
||||
all the line items and coatings from scratch.
|
||||
</p>
|
||||
<p>Cloning copies the following to a brand-new Pending job:</p>
|
||||
<ul class="mb-3">
|
||||
<li>Customer and all job settings (description, PO number, project name, special instructions, tags, priority, discount, oven settings)</li>
|
||||
<li>All line items with their coatings, colors, prep services, and pricing</li>
|
||||
</ul>
|
||||
<p>The following are <strong>not</strong> copied:</p>
|
||||
<ul class="mb-3">
|
||||
<li>Scheduled date, due date — you set these on the new job</li>
|
||||
<li>Assigned worker</li>
|
||||
<li>Photos, job notes, and time entries</li>
|
||||
<li>Invoice and payment records</li>
|
||||
</ul>
|
||||
<p>
|
||||
After cloning, the new job opens directly so you can review it, adjust dates, and save.
|
||||
A new unique job number (<code>JOB-YYMM-####</code>) is generated automatically.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section id="shop-display-and-board" class="mb-5">
|
||||
<h2 class="h4 fw-bold border-bottom pb-2 mb-3">
|
||||
<i class="bi bi-display text-primary me-2"></i>Shop Display and Priority Board
|
||||
@@ -827,6 +855,7 @@
|
||||
<a class="nav-link py-1 px-3 small text-body" href="#photos-notes">Photos and Notes</a>
|
||||
<a class="nav-link py-1 px-3 small text-body" href="#time-and-rework">Time Entries and Rework</a>
|
||||
<a class="nav-link py-1 px-3 small text-body" href="#job-templates">Job Templates</a>
|
||||
<a class="nav-link py-1 px-3 small text-body" href="#clone-job">Cloning a Job</a>
|
||||
<a class="nav-link py-1 px-3 small text-body" href="#shop-display-and-board">Shop Display & Priority Board</a>
|
||||
<a class="nav-link py-1 px-3 small text-body" href="#part-intake">Part Intake</a>
|
||||
<a class="nav-link py-1 px-3 small text-body" href="#shop-mobile">Shop Mobile</a>
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
</li>
|
||||
<li class="mb-2">Set the <strong>Quote Date</strong> (defaults to today) and the <strong>Expiry Date</strong> (defaults to the system's configured validity period).</li>
|
||||
<li class="mb-2">Add a <strong>Subject</strong> or description to identify the work being quoted.</li>
|
||||
<li class="mb-2">Optionally enter a <strong>Project Name</strong> — a short label that groups related work (e.g., “Fleet Refresh Q2”). It appears on the quote PDF and carries over to the job and invoice when converted.</li>
|
||||
<li class="mb-2">Add one or more <strong>Line Items</strong> — see the Quote Items section below for item types.</li>
|
||||
<li class="mb-2">Add any <strong>Notes</strong> for the customer (these appear on the printed quote).</li>
|
||||
<li class="mb-2">Add any internal <strong>Notes</strong> that are for your team only.</li>
|
||||
|
||||
Reference in New Issue
Block a user