first commit
This commit is contained in:
92
Views/Vehicle/Supply/_SupplyRecordModal.cshtml
Normal file
92
Views/Vehicle/Supply/_SupplyRecordModal.cshtml
Normal file
@@ -0,0 +1,92 @@
|
||||
@using MotoVaultPro.Helper
|
||||
@inject IConfigHelper config
|
||||
@inject ITranslationHelper translator
|
||||
@model SupplyRecordInput
|
||||
@{
|
||||
var isNew = Model.Id == 0;
|
||||
var userConfig = config.GetUserConfig(User);
|
||||
var userLanguage = userConfig.UserLanguage;
|
||||
}
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@(isNew ? translator.Translate(userLanguage, "Add New Supply Record") : translator.Translate(userLanguage, "Edit Supply Record"))<small style="display:none; @(isNew ? "" : "cursor:pointer;")" class="cached-banner ms-2 text-warning" onclick='@(isNew ? "" : $"showEditSupplyRecordModal({Model.Id}, true)" )'>@translator.Translate(userLanguage, "Unsaved Changes")</small></h5>
|
||||
<button type="button" class="btn-close" onclick="hideAddSupplyRecordModal()" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body" onkeydown="handleEnter(this)">
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-12">
|
||||
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
|
||||
<label for="supplyRecordDate">@translator.Translate(userLanguage,"Date")</label>
|
||||
<div class="input-group">
|
||||
<input type="text" id="supplyRecordDate" class="form-control" placeholder="@translator.Translate(userLanguage,"Date purchased")" value="@Model.Date">
|
||||
<span class="input-group-text"><i class="bi bi-calendar-event"></i></span>
|
||||
</div>
|
||||
<label for="supplyRecordPartNumber">@translator.Translate(userLanguage,"Part Number")</label>
|
||||
<input type="text" id="supplyRecordPartNumber" class="form-control" placeholder="@translator.Translate(userLanguage,"Part #/Model #/SKU #")" value="@(isNew ? "" : Model.PartNumber)">
|
||||
<label for="supplyRecordDescription">@translator.Translate(userLanguage,"Description")</label>
|
||||
<input type="text" id="supplyRecordDescription" class="form-control" placeholder="@translator.Translate(userLanguage,"Description of the Part/Supplies")" value="@Model.Description">
|
||||
<label for="supplyRecordSupplier">@translator.Translate(userLanguage,"Supplier/Vendor")</label>
|
||||
<input type="text" id="supplyRecordSupplier" class="form-control" placeholder="@translator.Translate(userLanguage,"Part Supplier")" value="@Model.PartSupplier">
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-12">
|
||||
<label for="supplyRecordQuantity">@translator.Translate(userLanguage,"Quantity")</label>
|
||||
<div class="input-group">
|
||||
<input type="text" inputmode="decimal" onkeydown="interceptDecimalKeys(event)" onkeyup="fixDecimalInput(this, 2)" id="supplyRecordQuantity" class="form-control" placeholder="@translator.Translate(userLanguage,"Quantity")" value="@(isNew ? "" : Model.Quantity.ToString("N2"))">
|
||||
<div class="input-group-text">
|
||||
<button type="button" class="btn btn-sm zero-y-padding btn-primary" onclick="replenishSupplies()"><i class="bi bi-plus"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-12">
|
||||
<label for="supplyRecordCost">@translator.Translate(userLanguage,"Cost")</label>
|
||||
<input type="text" inputmode="decimal" onkeydown="interceptDecimalKeys(event)" onkeyup="fixDecimalInput(this, 2)" id="supplyRecordCost" class="form-control" placeholder="@translator.Translate(userLanguage,"Cost")" value="@(isNew ? "" : Model.Cost)">
|
||||
</div>
|
||||
</div>
|
||||
<label for="supplyRecordTag">@translator.Translate(userLanguage, "Tags(optional)")</label>
|
||||
<select multiple class="form-select" id="supplyRecordTag">
|
||||
@foreach (string tag in Model.Tags)
|
||||
{
|
||||
<!option value="@tag">@tag</!option>
|
||||
}
|
||||
</select>
|
||||
@await Html.PartialAsync("_ExtraField", Model.ExtraFields)
|
||||
</div>
|
||||
<div class="col-md-6 col-12">
|
||||
<label for="supplyRecordNotes">@translator.Translate(userLanguage,"Notes(optional)")<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
|
||||
<textarea id="supplyRecordNotes" class="form-control" rows="5">@Model.Notes</textarea>
|
||||
<div>
|
||||
@await Html.PartialAsync("_UploadedFiles", Model.Files)
|
||||
@await Html.PartialAsync("_FileUploader", Model.Files.Any())
|
||||
</div>
|
||||
<div id="filesPendingUpload"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
@if (!isNew)
|
||||
{
|
||||
@if (Model.RequisitionHistory.Any())
|
||||
{
|
||||
<button type="button" class="btn btn-warning" onclick="toggleSupplyUsageHistory()"><i class="bi bi-clock-history"></i></button>
|
||||
}
|
||||
<button type="button" class="btn btn-danger" onclick="deleteSupplyRecord(@Model.Id)" style="margin-right:auto;">@translator.Translate(userLanguage,"Delete")</button>
|
||||
}
|
||||
<button type="button" class="btn btn-secondary" onclick="hideAddSupplyRecordModal()">@translator.Translate(userLanguage,"Cancel")</button>
|
||||
@if (isNew)
|
||||
{
|
||||
<button type="button" class="btn btn-primary" onclick="saveSupplyRecordToVehicle()">@translator.Translate(userLanguage,"Add New Supply Record")</button>
|
||||
}
|
||||
else if (!isNew)
|
||||
{
|
||||
<button type="button" class="btn btn-primary" onclick="saveSupplyRecordToVehicle(true)">@translator.Translate(userLanguage,"Edit Supply Record")</button>
|
||||
}
|
||||
</div>
|
||||
@await Html.PartialAsync("Supply/_SupplyRequisitionHistory", new SupplyRequisitionHistory { RequisitionHistory = Model.RequisitionHistory, CostInputId = "" })
|
||||
<script>
|
||||
function getSupplyRecordModelData() {
|
||||
return { id: @Model.Id}
|
||||
}
|
||||
</script>
|
||||
213
Views/Vehicle/Supply/_SupplyRecords.cshtml
Normal file
213
Views/Vehicle/Supply/_SupplyRecords.cshtml
Normal file
@@ -0,0 +1,213 @@
|
||||
@using MotoVaultPro.Helper
|
||||
@inject IConfigHelper config
|
||||
@inject ITranslationHelper translator
|
||||
@{
|
||||
var userConfig = config.GetUserConfig(User);
|
||||
var userLanguage = userConfig.UserLanguage;
|
||||
var enableCsvImports = userConfig.EnableCsvImports;
|
||||
var hideZero = userConfig.HideZero;
|
||||
var recordTags = Model.SelectMany(x => x.Tags).Distinct();
|
||||
var extraFields = new List<string>();
|
||||
if (userConfig.EnableExtraFieldColumns)
|
||||
{
|
||||
extraFields = Model.SelectMany(x => x.ExtraFields).Select(y => y.Name).Distinct().ToList();
|
||||
}
|
||||
var userColumnPreferences = userConfig.UserColumnPreferences.Where(x => x.Tab == ImportMode.SupplyRecord);
|
||||
}
|
||||
@model List<SupplyRecord>
|
||||
<div class="row">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="d-flex align-items-center flex-wrap">
|
||||
<span class="ms-2 badge bg-success" data-aggregate-type="count">@($"{translator.Translate(userLanguage, "# of Supply Records")}: {Model.Count()}")</span>
|
||||
<span class="ms-2 badge bg-primary" data-aggregate-type="sum">@($"{translator.Translate(userLanguage, "Total")}: {Model.Sum(x => x.Cost).ToString("C")}")</span>
|
||||
@foreach (string recordTag in recordTags)
|
||||
{
|
||||
<span onclick="filterTable('supply-tab-pane', this)" class="user-select-none ms-2 rounded-pill badge bg-secondary tagfilter" style="cursor:pointer;">@recordTag</span>
|
||||
}
|
||||
<datalist id="tagList">
|
||||
@foreach (string recordTag in recordTags)
|
||||
{
|
||||
<!option value="@recordTag"></!option>
|
||||
}
|
||||
</datalist>
|
||||
</div>
|
||||
<div>
|
||||
@if (enableCsvImports)
|
||||
{
|
||||
<div class="btn-group">
|
||||
<button onclick="showAddSupplyRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>@translator.Translate(userLanguage,"Add Supply Record")</button>
|
||||
<button type="button" class="btn btn-md btn-primary btn-md mt-1 mb-1 dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<span class="visually-hidden">Toggle Dropdown</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" href="#" onclick="showBulkImportModal('SupplyRecord')">@translator.Translate(userLanguage,"Import via CSV")</a></li>
|
||||
<li><a class="dropdown-item" href="#" onclick="exportVehicleData('SupplyRecord')">@translator.Translate(userLanguage,"Export to CSV")</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item" href="#" onclick="printTab()">@translator.Translate(userLanguage,"Print")</a></li>
|
||||
<li><a class="dropdown-item" href="#" onclick="searchTableRows('supply-tab-pane')">@translator.Translate(userLanguage, "Search")</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li>
|
||||
<div class="list-group-item">
|
||||
<input class="btn-check" type="checkbox" id="chkSelectMode">
|
||||
<label class="dropdown-item" for="chkSelectMode">@translator.Translate(userLanguage, "Select Mode")</label>
|
||||
</div>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><h6 class="dropdown-header">@translator.Translate(userLanguage, "Visible Columns")</h6></li>
|
||||
<li class="dropdown-item" draggable="true" ondragstart="handleTableColumnDragStart(event)" ondragover="handleTableColumnDragOver(event)" ondragend="handleTableColumnDragEnd('SupplyRecord')">
|
||||
<div class="list-group-item">
|
||||
<input class="form-check-input col-visible-toggle" data-column-toggle='date' onChange="showTableColumns(this, 'SupplyRecord')" type="checkbox" id="chkCol_Date" checked>
|
||||
<label class="form-check-label stretched-link" for="chkCol_Date">@translator.Translate(userLanguage, "Date")</label>
|
||||
</div>
|
||||
</li>
|
||||
<li class="dropdown-item" draggable="true" ondragstart="handleTableColumnDragStart(event)" ondragover="handleTableColumnDragOver(event)" ondragend="handleTableColumnDragEnd('SupplyRecord')">
|
||||
<div class="list-group-item">
|
||||
<input class="form-check-input col-visible-toggle" data-column-toggle='partnumber' onChange="showTableColumns(this, 'SupplyRecord')" type="checkbox" id="chkCol_PartNumber" checked>
|
||||
<label class="form-check-label stretched-link" for="chkCol_PartNumber">@translator.Translate(userLanguage, "Part Number")</label>
|
||||
</div>
|
||||
</li>
|
||||
<li class="dropdown-item" draggable="true" ondragstart="handleTableColumnDragStart(event)" ondragover="handleTableColumnDragOver(event)" ondragend="handleTableColumnDragEnd('SupplyRecord')">
|
||||
<div class="list-group-item">
|
||||
<input class="form-check-input col-visible-toggle" data-column-toggle='supplier' onChange="showTableColumns(this, 'SupplyRecord')" type="checkbox" id="chkCol_Supplier" checked>
|
||||
<label class="form-check-label stretched-link" for="chkCol_Supplier">@translator.Translate(userLanguage, "Supplier")</label>
|
||||
</div>
|
||||
</li>
|
||||
<li class="dropdown-item" draggable="true" ondragstart="handleTableColumnDragStart(event)" ondragover="handleTableColumnDragOver(event)" ondragend="handleTableColumnDragEnd('SupplyRecord')">
|
||||
<div class="list-group-item">
|
||||
<input class="form-check-input col-visible-toggle" data-column-toggle='description' onChange="showTableColumns(this, 'SupplyRecord')" type="checkbox" id="chkCol_Description" checked>
|
||||
<label class="form-check-label stretched-link" for="chkCol_Description">@translator.Translate(userLanguage, "Description")</label>
|
||||
</div>
|
||||
</li>
|
||||
<li class="dropdown-item" draggable="true" ondragstart="handleTableColumnDragStart(event)" ondragover="handleTableColumnDragOver(event)" ondragend="handleTableColumnDragEnd('SupplyRecord')">
|
||||
<div class="list-group-item">
|
||||
<input class="form-check-input col-visible-toggle" data-column-toggle='quantity' onChange="showTableColumns(this, 'SupplyRecord')" type="checkbox" id="chkCol_Quantity" checked>
|
||||
<label class="form-check-label stretched-link" for="chkCol_Quantity">@translator.Translate(userLanguage, "Quantity")</label>
|
||||
</div>
|
||||
</li>
|
||||
<li class="dropdown-item" draggable="true" ondragstart="handleTableColumnDragStart(event)" ondragover="handleTableColumnDragOver(event)" ondragend="handleTableColumnDragEnd('SupplyRecord')">
|
||||
<div class="list-group-item">
|
||||
<input class="form-check-input col-visible-toggle" data-column-toggle='cost' onChange="showTableColumns(this, 'SupplyRecord')" type="checkbox" id="chkCol_Cost" checked>
|
||||
<label class="form-check-label stretched-link" for="chkCol_Cost">@translator.Translate(userLanguage, "Cost")</label>
|
||||
</div>
|
||||
</li>
|
||||
<li class="dropdown-item" draggable="true" ondragstart="handleTableColumnDragStart(event)" ondragover="handleTableColumnDragOver(event)" ondragend="handleTableColumnDragEnd('SupplyRecord')">
|
||||
<div class="list-group-item">
|
||||
<input class="form-check-input col-visible-toggle" data-column-toggle='attachments' onChange="showTableColumns(this, 'SupplyRecord')" type="checkbox" id="chkCol_Attachment">
|
||||
<label class="form-check-label stretched-link" for="chkCol_Attachment">@translator.Translate(userLanguage, "Attachments")</label>
|
||||
</div>
|
||||
</li>
|
||||
<li class="dropdown-item" draggable="true" ondragstart="handleTableColumnDragStart(event)" ondragover="handleTableColumnDragOver(event)" ondragend="handleTableColumnDragEnd('SupplyRecord')">
|
||||
<div class="list-group-item">
|
||||
<input class="form-check-input col-visible-toggle" data-column-toggle='notes' onChange="showTableColumns(this, 'SupplyRecord')" type="checkbox" id="chkCol_Notes" checked>
|
||||
<label class="form-check-label stretched-link" for="chkCol_Notes">@translator.Translate(userLanguage, "Notes")</label>
|
||||
</div>
|
||||
</li>
|
||||
@foreach (string extraFieldColumn in extraFields)
|
||||
{
|
||||
var elementId = Guid.NewGuid();
|
||||
<li class="dropdown-item" draggable="true" ondragstart="handleTableColumnDragStart(event)" ondragover="handleTableColumnDragOver(event)" ondragend="handleTableColumnDragEnd('SupplyRecord')">
|
||||
<div class="list-group-item">
|
||||
<input class="form-check-input col-visible-toggle" data-column-toggle='@extraFieldColumn' onChange="showTableColumns(this, 'SupplyRecord')" type="checkbox" id="@elementId">
|
||||
<label class="form-check-label stretched-link" for="@elementId">@extraFieldColumn</label>
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button onclick="showAddSupplyRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>@translator.Translate(userLanguage,"Add Supply Record")</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row vehicleDetailTabContainer">
|
||||
<div class="col-12">
|
||||
<div class="row mt-2 showOnPrint">
|
||||
<div class="d-flex">
|
||||
<img src="@config.GetLogoUrl()" class="motovaultpro-logo" />
|
||||
</div>
|
||||
</div>
|
||||
<table class="table table-hover">
|
||||
<thead class="sticky-top">
|
||||
<tr class="d-flex">
|
||||
<th scope="col" class="col-2 flex-grow-1 col-xl-1 text-truncate" data-column="date">@translator.Translate(userLanguage, "Date")</th>
|
||||
<th scope="col" class="col-2 flex-grow-1 flex-shrink-1 text-truncate" data-column="partnumber">@translator.Translate(userLanguage, "Part #")</th>
|
||||
<th scope="col" class="col-2 flex-grow-1 flex-shrink-1 text-truncate" data-column="supplier">@translator.Translate(userLanguage, "Supplier")</th>
|
||||
<th scope="col" class="col-2 flex-grow-1 col-xl-3 text-truncate" data-column="description">@translator.Translate(userLanguage, "Description")</th>
|
||||
<th scope="col" class="col-1 flex-grow-1 flex-shrink-1 text-truncate" data-column="quantity" onclick="toggleSort('supply-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Quantity")</th>
|
||||
<th scope="col" class="col-1 flex-grow-1 flex-shrink-1 text-truncate" data-column="cost" onclick="toggleSort('supply-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Cost")</th>
|
||||
<th scope="col" class="col-1 flex-grow-1 flex-shrink-1 text-truncate" style='display:none;' data-column="attachments">@translator.Translate(userLanguage, "Attachments")</th>
|
||||
<th scope="col" class="col-2 flex-grow-1 flex-shrink-1 text-truncate" data-column="notes">@translator.Translate(userLanguage, "Notes")</th>
|
||||
@foreach (string extraFieldColumn in extraFields)
|
||||
{
|
||||
<th scope="col" style='display:none;' class="col-2 flex-grow-1 flex-shrink-1 text-truncate" data-column="@extraFieldColumn">@extraFieldColumn</th>
|
||||
}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (SupplyRecord supplyRecord in Model)
|
||||
{
|
||||
<tr class="d-flex user-select-none" style="cursor:pointer;" onmouseup="stopEvent()" ontouchstart="detectRowLongTouch(this)" ontouchend="detectRowTouchEndPremature(this)" data-rowId="@supplyRecord.Id" oncontextmenu="showTableContextMenu(this)" onmousemove="rangeMouseMove(this)" onclick="handleTableRowClick(this, showEditSupplyRecordModal,@supplyRecord.Id)" data-tags='@string.Join(" ", supplyRecord.Tags)'>
|
||||
<td class="col-2 flex-grow-1 col-xl-1 text-truncate" data-column="date">@supplyRecord.Date.ToShortDateString()</td>
|
||||
<td class="col-2 flex-grow-1 flex-shrink-1 text-truncate" data-column="partnumber">@supplyRecord.PartNumber</td>
|
||||
<td class="col-2 flex-grow-1 flex-shrink-1 text-truncate" data-column="supplier">@supplyRecord.PartSupplier</td>
|
||||
<td class="col-2 flex-grow-1 col-xl-3 text-truncate" data-column="description">@supplyRecord.Description</td>
|
||||
<td class="col-1 flex-grow-1 flex-shrink-1 text-truncate" data-column="quantity">@supplyRecord.Quantity</td>
|
||||
<td class="col-1 flex-grow-1 flex-shrink-1 text-truncate" data-column="cost" data-record-type="cost">@(StaticHelper.HideZeroCost(supplyRecord.Cost, hideZero))</td>
|
||||
<td class="col-1 flex-grow-1 flex-shrink-1 text-truncate" style='display:none;' data-column="attachments">@await Html.PartialAsync("_AttachmentColumn", supplyRecord.Files)</td>
|
||||
<td class="col-2 flex-grow-1 flex-shrink-1 text-truncate" data-column="notes">@StaticHelper.TruncateStrings(supplyRecord.Notes)</td>
|
||||
@foreach (string extraFieldColumn in extraFields)
|
||||
{
|
||||
<td class="col-2 flex-grow-1 flex-shrink-1 text-truncate" style='display:none;' data-column="@extraFieldColumn">
|
||||
@{
|
||||
var extraFieldValue = supplyRecord.ExtraFields.Where(x => x.Name == extraFieldColumn)?.FirstOrDefault()?.Value ?? "";
|
||||
if (!string.IsNullOrWhiteSpace(extraFieldValue) && Uri.IsWellFormedUriString(extraFieldValue, UriKind.Absolute))
|
||||
{
|
||||
<a href="@extraFieldValue" onclick="noPropagation()" target="_blank">@StaticHelper.TruncateStrings(extraFieldValue)</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
@extraFieldValue
|
||||
}
|
||||
}
|
||||
</td>
|
||||
}
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr class="d-flex">
|
||||
<td class="col-12 showOnPrint motovaultpro-report-banner">
|
||||
@StaticHelper.ReportNote
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="modal fade" data-bs-focus="false" id="supplyRecordModal" tabindex="-1" role="dialog" aria-hidden="true" onpaste="handleModalPaste(event)">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content" id="supplyRecordModalContent">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="table-context-menu dropdown-menu" style="display:none;">
|
||||
<li><a class="context-menu-multiple context-menu-select-all dropdown-item" href="#" onclick="selectAllRows()"><div class="d-flex justify-content-between"><span class="me-5">@translator.Translate(userLanguage, "Select All")</span><i class="bi bi-check-square"></i></div></a></li>
|
||||
<li><a class="context-menu-multiple context-menu-deselect-all dropdown-item" href="#" onclick="clearSelectedRows()"><div class="d-flex justify-content-between"><span class="me-5">@translator.Translate(userLanguage, "Deselect All")</span><i class="bi bi-x-square"></i></div></a></li>
|
||||
<li><hr class="context-menu-multiple dropdown-divider"></li>
|
||||
<li><a class="dropdown-item" href="#" onclick="duplicateRecords(selectedRow, 'SupplyRecord')"><div class="d-flex justify-content-between"><span class="me-5">@translator.Translate(userLanguage, "Duplicate")</span><i class="bi bi-copy"></i></div></a></li>
|
||||
<li><a class="dropdown-item" href="#" onclick="duplicateRecordsToOtherVehicles(selectedRow, 'SupplyRecord')"><div class="d-flex justify-content-between"><span class="me-5">@translator.Translate(userLanguage, "Duplicate To Vehicle")</span><i class="bi bi-copy"></i></div></a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item" href="#" onclick="printTabStickers(selectedRow, 'SupplyRecord')"><div class="d-flex justify-content-between"><span class="me-5">@translator.Translate(userLanguage, "Print")</span><i class="bi bi-printer"></i></div></a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item text-danger" href="#" onclick="deleteRecords(selectedRow, 'SupplyRecord')"><div class="d-flex justify-content-between"><span class="me-5">@translator.Translate(userLanguage, "Delete")</span><i class="bi bi-trash"></i></div></a></li>
|
||||
</ul>
|
||||
@if (userColumnPreferences.Any())
|
||||
{
|
||||
@await Html.PartialAsync("_UserColumnPreferences", userColumnPreferences)
|
||||
}
|
||||
106
Views/Vehicle/Supply/_SupplyRequisitionHistory.cshtml
Normal file
106
Views/Vehicle/Supply/_SupplyRequisitionHistory.cshtml
Normal file
@@ -0,0 +1,106 @@
|
||||
@using MotoVaultPro.Helper
|
||||
@inject IConfigHelper config
|
||||
@inject ITranslationHelper translator
|
||||
@model SupplyRequisitionHistory
|
||||
@{
|
||||
var userConfig = config.GetUserConfig(User);
|
||||
var userLanguage = userConfig.UserLanguage;
|
||||
var showDelete = Model.RequisitionHistory.All(x => x.Id != default);
|
||||
var showPartNumber = Model.RequisitionHistory.Any(x => !string.IsNullOrWhiteSpace(x.PartNumber));
|
||||
}
|
||||
<script>
|
||||
var supplyUsageHistory = [];
|
||||
var deletedSupplyUsageHistory = [];
|
||||
function subtractFromCostInput(costToSubtract){
|
||||
let costInputId = '@Model.CostInputId';
|
||||
let costInput = $(`#${costInputId}`);
|
||||
let newCostAmount = globalParseFloat(costInput.val()) - globalParseFloat(costToSubtract);
|
||||
if (newCostAmount < 0){
|
||||
newCostAmount = 0;
|
||||
}
|
||||
costInput.val(globalFloatToString(newCostAmount.toFixed(2)));
|
||||
}
|
||||
function deleteSupplyUsageHistory(supplyId, quantity, cost, supplyRow){
|
||||
deletedSupplyUsageHistory.push({
|
||||
id: decodeHTMLEntities(supplyId),
|
||||
quantity: decodeHTMLEntities(quantity),
|
||||
cost: decodeHTMLEntities(cost)
|
||||
});
|
||||
let supplyIndexToRemove = supplyUsageHistory.findIndex(x=>x.id == decodeHTMLEntities(supplyId) && x.quantity == decodeHTMLEntities(quantity) && x.cost == decodeHTMLEntities(cost));
|
||||
if (supplyIndexToRemove > -1){
|
||||
supplyUsageHistory.splice(supplyIndexToRemove, 1);
|
||||
}
|
||||
$(supplyRow).parents(".supply-row").remove();
|
||||
//update cost input value
|
||||
subtractFromCostInput(decodeHTMLEntities(cost));
|
||||
}
|
||||
</script>
|
||||
<div id="supplyUsageHistoryModalContainer" class="d-none">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@translator.Translate(userLanguage, "Supply Requisition History")</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
@if (Model.RequisitionHistory.Any())
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-12" style="max-height:50vh; overflow-y:auto;">
|
||||
<table class="table table-hover">
|
||||
<thead class="sticky-top">
|
||||
<tr class="d-flex">
|
||||
<th scope="col" class="col-2">@translator.Translate(userLanguage, "Date")</th>
|
||||
@if(showPartNumber){
|
||||
<th scope="col" class="col-2">@translator.Translate(userLanguage, "Part Number")</th>
|
||||
<th scope="col" class="@(showDelete ? "col-2" : "col-4")">@translator.Translate(userLanguage, "Description")</th>
|
||||
} else
|
||||
{
|
||||
<th scope="col" class="@(showDelete ? "col-4" : "col-6")">@translator.Translate(userLanguage, "Description")</th>
|
||||
}
|
||||
<th scope="col" class="col-2">@translator.Translate(userLanguage, "Quantity")</th>
|
||||
<th scope="col" class="col-2">@translator.Translate(userLanguage, "Cost")</th>
|
||||
@if (showDelete){
|
||||
<th scope="col" class="col-2">@translator.Translate(userLanguage, "Delete")</th>
|
||||
}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (SupplyUsageHistory usageHistory in Model.RequisitionHistory)
|
||||
{
|
||||
<script>
|
||||
supplyUsageHistory.push({ id: decodeHTMLEntities("@usageHistory.Id"), date: decodeHTMLEntities("@usageHistory.Date.ToShortDateString()"), partNumber: decodeHTMLEntities('@usageHistory.PartNumber'), description: decodeHTMLEntities("@usageHistory.Description"), quantity: decodeHTMLEntities("@usageHistory.Quantity.ToString("F")"), cost: decodeHTMLEntities("@usageHistory.Cost.ToString("F")") })
|
||||
</script>
|
||||
<tr class="d-flex supply-row">
|
||||
<td class="col-2">@StaticHelper.TruncateStrings(usageHistory.Date.ToShortDateString())</td>
|
||||
@if (showPartNumber)
|
||||
{
|
||||
<td class="col-2 text-truncate">@StaticHelper.TruncateStrings(usageHistory.PartNumber)</td>
|
||||
<td class="@(showDelete ? "col-2" : "col-4") text-truncate">@StaticHelper.TruncateStrings(usageHistory.Description)</td>
|
||||
} else
|
||||
{
|
||||
<td class="@(showDelete ? "col-4" : "col-6") text-truncate">@StaticHelper.TruncateStrings(usageHistory.Description, 50)</td>
|
||||
}
|
||||
<td class="col-2">@usageHistory.Quantity.ToString("F")</td>
|
||||
<td class="col-2">@usageHistory.Cost.ToString("C2")</td>
|
||||
@if (showDelete){
|
||||
<td class="col-2">
|
||||
<button type="button" class="btn btn-danger" onclick="deleteSupplyUsageHistory('@usageHistory.Id.ToString()', '@usageHistory.Quantity.ToString("F")', '@usageHistory.Cost.ToString("F")', this)"><i class="bi bi-trash"></i></button>
|
||||
</td>
|
||||
}
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="text-center">
|
||||
<h4>@translator.Translate(userLanguage, "No supply requisitions in history")</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
133
Views/Vehicle/Supply/_SupplyStore.cshtml
Normal file
133
Views/Vehicle/Supply/_SupplyStore.cshtml
Normal file
@@ -0,0 +1,133 @@
|
||||
@using MotoVaultPro.Helper
|
||||
@inject IConfigHelper config
|
||||
@inject ITranslationHelper translator
|
||||
@{
|
||||
var userConfig = config.GetUserConfig(User);
|
||||
var userLanguage = userConfig.UserLanguage;
|
||||
}
|
||||
@model SupplyStore
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<a onclick="showSuppliesModal()" class="btn btn-link">@translator.Translate(userLanguage,Model.AdditionalSupplies ? "Choose Additional Supplies" : "Choose Supplies")</a>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
resetSuppliesModal();
|
||||
function GetCaller() {
|
||||
return {
|
||||
tab: '@Model.Tab',
|
||||
addToSum: @Model.AdditionalSupplies.ToString().ToLower()
|
||||
};
|
||||
}
|
||||
function resetSuppliesModal() {
|
||||
$("#inputSuppliesModalContent").html("");
|
||||
}
|
||||
function setCostInputWithSupplySum(selectedSum, input){
|
||||
var addToSum = GetCaller().addToSum;
|
||||
if (addToSum){
|
||||
//sum of all requisitioned supplies
|
||||
var currentSum = supplyUsageHistory.length > 0 ? supplyUsageHistory.map(x => globalParseFloat(x.cost)).reduce((a,b) => a + b) : 0;
|
||||
selectedSum = globalParseFloat(selectedSum);
|
||||
var newSum = currentSum + selectedSum;
|
||||
input.val(globalFloatToString(newSum.toFixed(2)));
|
||||
} else {
|
||||
input.val(selectedSum);
|
||||
}
|
||||
}
|
||||
function selectSupplies() {
|
||||
var selectedSupplyResult = getSuppliesAndQuantity();
|
||||
var caller = GetCaller().tab;
|
||||
switch (caller) {
|
||||
case "ServiceRecord":
|
||||
setCostInputWithSupplySum(selectedSupplyResult.totalSum, $('#serviceRecordCost'))
|
||||
break;
|
||||
case "RepairRecord":
|
||||
setCostInputWithSupplySum(selectedSupplyResult.totalSum, $('#collisionRecordCost'))
|
||||
break;
|
||||
case "UpgradeRecord":
|
||||
setCostInputWithSupplySum(selectedSupplyResult.totalSum, $('#upgradeRecordCost'))
|
||||
break;
|
||||
case "PlanRecord":
|
||||
case "PlanRecordTemplate":
|
||||
setCostInputWithSupplySum(selectedSupplyResult.totalSum, $('#planRecordCost'))
|
||||
break;
|
||||
}
|
||||
selectedSupplies = getSuppliesAndQuantity().selectedSupplies;
|
||||
copySuppliesAttachments = $("#inputCopySuppliesAttachments").is(':checked');
|
||||
hideSuppliesModal();
|
||||
}
|
||||
function hideParentModal(){
|
||||
var caller = GetCaller().tab;
|
||||
switch (caller) {
|
||||
case "ServiceRecord":
|
||||
$('#serviceRecordModal').modal('hide');
|
||||
break;
|
||||
case "RepairRecord":
|
||||
$('#collisionRecordModal').modal('hide');
|
||||
break;
|
||||
case "UpgradeRecord":
|
||||
$('#upgradeRecordModal').modal('hide');
|
||||
break;
|
||||
case "PlanRecord":
|
||||
case "PlanRecordTemplate":
|
||||
$('#planRecordModal').modal('hide');
|
||||
break;
|
||||
}
|
||||
}
|
||||
function showParentModal() {
|
||||
var caller = GetCaller().tab;
|
||||
switch (caller) {
|
||||
case "ServiceRecord":
|
||||
$('#serviceRecordModal').modal('show');
|
||||
break;
|
||||
case "RepairRecord":
|
||||
$('#collisionRecordModal').modal('show');
|
||||
break;
|
||||
case "UpgradeRecord":
|
||||
$('#upgradeRecordModal').modal('show');
|
||||
break;
|
||||
case "PlanRecord":
|
||||
case "PlanRecordTemplate":
|
||||
$('#planRecordModal').modal('show');
|
||||
break;
|
||||
}
|
||||
}
|
||||
function showSuppliesModal() {
|
||||
if ($("#inputSuppliesModalContent").html() == "") {
|
||||
getSupplies();
|
||||
} else {
|
||||
hideParentModal();
|
||||
$('#inputSuppliesModal').modal('show');
|
||||
}
|
||||
}
|
||||
function getSupplies() {
|
||||
var caller = GetCaller().tab;
|
||||
if (caller == 'PlanRecordTemplate') {
|
||||
var planRecordTemplateId = getPlanRecordModelData().id;
|
||||
$.get(`/Vehicle/GetSupplyRecordsForPlanRecordTemplate?planRecordTemplateId=${planRecordTemplateId}`, function (data) {
|
||||
if (data) {
|
||||
hideParentModal();
|
||||
$("#inputSuppliesModalContent").html(data);
|
||||
$('#inputSuppliesModal').modal('show');
|
||||
recalculateTotal();
|
||||
if (copySuppliesAttachments) {
|
||||
$('#inputCopySuppliesAttachments').attr('checked', true);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
var vehicleId = GetVehicleId().vehicleId;
|
||||
$.get(`/Vehicle/GetSupplyRecordsForRecordsByVehicleId?vehicleId=${vehicleId}`, function (data) {
|
||||
if (data) {
|
||||
hideParentModal();
|
||||
$("#inputSuppliesModalContent").html(data);
|
||||
$('#inputSuppliesModal').modal('show');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
function hideSuppliesModal() {
|
||||
$('#inputSuppliesModal').modal('hide');
|
||||
showParentModal();
|
||||
}
|
||||
</script>
|
||||
147
Views/Vehicle/Supply/_SupplyUsage.cshtml
Normal file
147
Views/Vehicle/Supply/_SupplyUsage.cshtml
Normal file
@@ -0,0 +1,147 @@
|
||||
@using MotoVaultPro.Helper
|
||||
@inject IConfigHelper config
|
||||
@inject ITranslationHelper translator
|
||||
@{
|
||||
var userConfig = config.GetUserConfig(User);
|
||||
var userLanguage = userConfig.UserLanguage;
|
||||
var recordTags = Model.Supplies.SelectMany(x => x.Tags).Distinct();
|
||||
}
|
||||
@model SupplyUsageViewModel
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@translator.Translate(userLanguage,"Select Supplies")</h5>
|
||||
<button type="button" class="btn-close" onclick="hideSuppliesModal()" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
@if (Model.Supplies.Any())
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-12" style="max-height:50vh; overflow-y:auto;" id="supplies-table">
|
||||
<div class="alert alert-warning" role="alert">
|
||||
@translator.Translate(userLanguage,"Supplies are requisitioned immediately after the record is saved.")
|
||||
</div>
|
||||
<div class="d-flex align-items-center flex-wrap">
|
||||
@foreach (string recordTag in recordTags)
|
||||
{
|
||||
<span onclick="filterTable('supplies-table', this)" class="user-select-none ms-2 rounded-pill badge bg-secondary tagfilter" style="cursor:pointer;">@recordTag</span>
|
||||
}
|
||||
</div>
|
||||
<table class="table table-hover">
|
||||
<thead class="sticky-top">
|
||||
<tr class="d-flex">
|
||||
<th scope="col" class="col-1"></th>
|
||||
<th scope="col" class="col-3 col-sm-2 text-truncate flex-grow">@translator.Translate(userLanguage,"Quantity")</th>
|
||||
<th scope="col" class="col-1 col-sm-2 text-truncate flex-shrink">@translator.Translate(userLanguage, "In Stock")</th>
|
||||
<th scope="col" class="col-2 text-truncate flex-shrink">@translator.Translate(userLanguage, "Part Number")</th>
|
||||
<th scope="col" class="col-3 text-truncate flex-shrink">@translator.Translate(userLanguage, "Description")</th>
|
||||
<th scope="col" class="col-2 text-truncate flex-shrink">@translator.Translate(userLanguage, "Unit Cost")</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (SupplyRecord supplyRecord in Model.Supplies)
|
||||
{
|
||||
var supplyUsage = Model.Usage.Where(x => x.SupplyId == supplyRecord.Id).SingleOrDefault();
|
||||
<tr class="d-flex" id="supplyRows" data-tags='@string.Join(" ", supplyRecord.Tags)'>
|
||||
<td class="col-1"><input class="form-check-input" type="checkbox" onchange="toggleQuantityFieldDisabled(this)" value="@supplyRecord.Id" @(supplyUsage == default ? "" : "checked")></td>
|
||||
<td class="col-3 col-sm-2 flex-grow text-truncate supplyquantityinput"><input type="text" inputmode="decimal" onkeydown="interceptDecimalKeys(event)" onkeyup="fixDecimalInput(this, 2)" @(supplyUsage == default ? "disabled" : "") value="@(supplyUsage == default ? "" : supplyUsage.Quantity)" onchange="recalculateTotal()" class="form-control"></td>
|
||||
<td class="col-1 col-sm-2 flex-shrink text-truncate supplyquantity">@supplyRecord.Quantity</td>
|
||||
<td class="col-2 flex-shrink text-truncate">@StaticHelper.TruncateStrings(supplyRecord.PartNumber)</td>
|
||||
<td class="col-3 flex-shrink text-truncate">@StaticHelper.TruncateStrings(supplyRecord.Description)</td>
|
||||
<td class="col-2 flex-shrink text-truncate supplyprice">@((supplyRecord.Quantity > 0 ? supplyRecord.Cost / supplyRecord.Quantity : 0).ToString("F"))</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="text-center">
|
||||
<h4>@translator.Translate(userLanguage, "No supplies with quantities greater than 0 is found.")</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<span id="supplySumLabel" style="margin-right:auto;">Total: 0.00</span>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" role="switch" id="inputCopySuppliesAttachments">
|
||||
<label class="form-check-label" for="inputCopySuppliesAttachments">@translator.Translate(userLanguage, "Copy Attachments")</label>
|
||||
</div>
|
||||
<button type="button" class="btn btn-secondary" onclick="hideSuppliesModal()">@translator.Translate(userLanguage, "Cancel")</button>
|
||||
<button type="button" class="btn btn-primary" disabled id="selectSuppliesButton" onclick="selectSupplies()">@translator.Translate(userLanguage, "Select")</button>
|
||||
</div>
|
||||
<script>
|
||||
function recalculateTotal() {
|
||||
setDebounce(getSuppliesAndQuantity);
|
||||
}
|
||||
function toggleQuantityFieldDisabled(e) {
|
||||
var textField = getTextFieldFromCheckBox(e);
|
||||
var isChecked = $(e).is(":checked");
|
||||
textField.attr('disabled', !isChecked);
|
||||
if (!isChecked) {
|
||||
textField.removeClass("is-invalid");
|
||||
}
|
||||
recalculateTotal();
|
||||
}
|
||||
function getTextFieldFromCheckBox(elem) {
|
||||
var textField = $(elem.parentElement.parentElement).find('.supplyquantityinput input[type=text]')[0];
|
||||
return $(textField);
|
||||
}
|
||||
function getInStockFieldFromCheckBox(elem) {
|
||||
var textField = $(elem.parentElement.parentElement).find('.supplyquantity')[0];
|
||||
return $(textField);
|
||||
}
|
||||
function getPriceFieldFromCheckBox(elem) {
|
||||
var textField = $(elem.parentElement.parentElement).find('.supplyprice')[0];
|
||||
return $(textField);
|
||||
}
|
||||
function getSuppliesAndQuantity() {
|
||||
var totalSum = 0;
|
||||
var hasError = false;
|
||||
var selectedSupplies = $("#supplyRows :checked").map(function () {
|
||||
var textField = getTextFieldFromCheckBox(this);
|
||||
var inStock = getInStockFieldFromCheckBox(this);
|
||||
var priceField = getPriceFieldFromCheckBox(this);
|
||||
var requestedQuantity = globalParseFloat(textField.val());
|
||||
var inStockQuantity = globalParseFloat(inStock.text());
|
||||
var unitPrice = globalParseFloat(priceField.text());
|
||||
//validation
|
||||
if (isNaN(requestedQuantity) || requestedQuantity > inStockQuantity || requestedQuantity <= 0) {
|
||||
textField.addClass("is-invalid");
|
||||
hasError = true;
|
||||
} else {
|
||||
textField.removeClass("is-invalid");
|
||||
}
|
||||
//calculate sum.
|
||||
var sum = requestedQuantity * unitPrice;
|
||||
totalSum += sum;
|
||||
return {
|
||||
supplyId: this.value,
|
||||
quantity: textField.val()
|
||||
};
|
||||
});
|
||||
if (isNaN(totalSum) || hasError) {
|
||||
$("#supplySumLabel").text(`Total: 0.00`);
|
||||
} else {
|
||||
totalSum = totalSum.toFixed(2);
|
||||
var parsedFloat = globalFloatToString(totalSum);
|
||||
$("#supplySumLabel").text(`Total: ${parsedFloat}`);
|
||||
}
|
||||
$("#selectSuppliesButton").attr('disabled', (hasError || selectedSupplies.toArray().length == 0));
|
||||
if (!hasError) {
|
||||
return {
|
||||
totalSum: globalFloatToString(totalSum),
|
||||
selectedSupplies: selectedSupplies.toArray()
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
totalSum: 0,
|
||||
selectedSupplies: []
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user