This commit is contained in:
2025-12-24 10:55:40 +08:00
parent eec9d6045c
commit 752bb23571
25 changed files with 2348 additions and 816 deletions

View File

@@ -1,4 +1,4 @@
import { createElement, Dropdown, Popup, showAlert, createIcon, DateSelector, showConfirm, Grid, OptionBase } from "../ui";
import { createElement, Dropdown, Popup, showAlert, createIcon, DateSelector, showConfirm, Grid, OptionBase, createCheckbox } from "../ui";
import { nullOrEmpty } from "../utility";
import AssetSelector from "./assetSelector";
@@ -131,6 +131,11 @@ export default class AddWorkOrder extends OptionBase {
* @private
* @type {Dropdown}
*/
dropAssetStatus: null,
/**
* @private
* @type {DateSelector}
*/
dropAssignedTo: null,
/**
* @private
@@ -221,6 +226,18 @@ export default class AddWorkOrder extends OptionBase {
Status: -1
};
}
let assetstatus = el.dropAssetStatus.selected;
if (assetstatus != null) {
assetstatus = {
AssetCustomStatus: assetstatus.Id,
AssetCustomStatusType: assetstatus.Type,
AssetCustomStatusName: assetstatus.Name
};
} else {
assetstatus = {
AssetCustomStatus: -1
};
}
let machine = this._var.asset;
if (machine == null) {
showAlert(title, this.r('FLTL_00311', 'Asset cannot be empty.')).then(() => this._var.container.querySelector('.wo-asset>svg')?.focus());
@@ -238,7 +255,7 @@ export default class AddWorkOrder extends OptionBase {
AssignedTo: el.dropAssignedTo.selected?.IID ?? '',
AdvisorId: el.dropAdvisor.selected?.Key ?? '',
LocationId: el.dropLocation.selected?.ID || -1,
DepartmentId: el.dropDepartment.selected?.Id || -1,
DepartmentId: el.dropDepartment.checked?.Id || -1,
AssetID: machine.Id,
VIN: machine.VIN,
AssetName: machine.DisplayName,
@@ -250,7 +267,8 @@ export default class AddWorkOrder extends OptionBase {
LaborCost: -1,
HourlyRate: -1,
InspectionTemplateId: -1,
...status
...status,
...assetstatus
};
item.CustomerId = this._var.customer?.Id ?? -1;
item.Contacts = this._var.contacts ?? [];
@@ -259,19 +277,18 @@ export default class AddWorkOrder extends OptionBase {
showAlert(title, this.r('FLTL_00602', 'Complaint is required.')).then(() => el.textComplaint.focus());
return null;
}
item.MeterType = machine.OnRoad ? 'Odometer' : 'HourMeter';
if (el.dropStatus.selected?.Completed) {
if (!el.dateCompleted.element.validity.valid) {
showAlert(title, this.r('FLTL_00613', 'Completed Date cannot be empty.')).then(() => el.dateCompleted.element.focus());
return null;
}
if (machine.OnRoad) {
item.MeterType = 'Odometer';
if (nullOrEmpty(item.Odometer) || isNaN(item.Odometer) || item.Odometer < 0) {
showAlert(title, this.r('FLTL_02044', 'Odometer format error.')).then(() => el.inputOdometer.focus());
return null;
}
} else {
item.MeterType = 'HourMeter';
if (nullOrEmpty(item.HourMeter) || isNaN(item.HourMeter) || item.HourMeter < 0) {
showAlert(title, this.r('FLTL_01516', 'Hour Meter format error.')).then(() => el.inputHours.focus());
return null;
@@ -284,6 +301,9 @@ export default class AddWorkOrder extends OptionBase {
async show() {
const option = this._option;
const allowCustomer = option.allowCustomer === true;
const allowCommunicate = option.allowCommunicate === true;
const commReadOnly = option.commReadOnly === true;
const msgVariables = option.msgVariables || [];
const title = this.r('FLTL_02078', 'Open Work Order');
const tabIndex = Math.max.apply(null, [...document.querySelectorAll('[tabindex]')].map(e => e.tabIndex ?? 0)) + 3;
const textComplaint = createElement('textarea', textarea => {
@@ -333,6 +353,16 @@ export default class AddWorkOrder extends OptionBase {
{ value: 'Mile', text: this.r('FLTL_01922', 'Mile') },
{ value: 'Kilometre', text: this.r('FLTL_01694', 'Kilometer') }
]
const dropAssetStatus = new Dropdown({
tabIndex: tabIndex + 5,
htmlTemplate: it => createElement('span', 'wo-color-line',
createElement('em', em => em.style.backgroundColor = it.Color),
createElement('label', label => label.innerText = it.Name)
),
search: true,
textKey: 'Name',
valueKey: 'Id'
});
const dropAssignedTo = new Dropdown({
tabIndex: tabIndex + 10,
selected: '',
@@ -360,6 +390,18 @@ export default class AddWorkOrder extends OptionBase {
valueKey: 'Id',
textKey: 'Name'
});
const dropMSGVariables = new Dropdown({
selected: -1,
search: true,
valueKey: 'Id',
textKey: 'Name'
});
dropMSGVariables.source = msgVariables.map(v => { return { Id: v.Name, Name: v.Name }; });
const textMessage = createElement('textarea', textarea => {
textarea.className = 'ui-text wo-message';
});
const dropMSGVariablesElement = dropMSGVariables.create();
dropMSGVariablesElement.style.width = '200px';
// save variables
this._var.el = {
@@ -370,6 +412,7 @@ export default class AddWorkOrder extends OptionBase {
inputHours,
inputOdometer,
dropOdometerUnit,
dropAssetStatus,
dropAssignedTo,
dropAdvisor,
dropLocation,
@@ -415,6 +458,7 @@ export default class AddWorkOrder extends OptionBase {
el.inputOdometer.value = '';
el.dropOdometerUnit.select('Mile');
}
el.dropAssetStatus.select(it.CustomStatus);
}
}
this._var.asset = it;
@@ -537,6 +581,11 @@ export default class AddWorkOrder extends OptionBase {
patternValidation(inputOdometer, '\\d+\\.?\\d*'),
dropOdometerUnit.create()
),
createElement('span', span => {
span.className = 'wo-title';
span.innerText = this.r('FLTL_03590', 'Asset Availability:');
}),
dropAssetStatus.create(),
createElement('span', span => {
span.className = 'wo-title';
span.innerText = this.r('FLTL_00382', 'Assigned Tech:');
@@ -606,7 +655,7 @@ export default class AddWorkOrder extends OptionBase {
buttons: [
{
key: 'open',
text: title,
text: this.r('FLTL_00700', 'Create Work Order'),
trigger: async () => {
popup.loading = true;
try {
@@ -620,7 +669,7 @@ export default class AddWorkOrder extends OptionBase {
return false;
}
const next = await showConfirm(title, this.r('FLTL_02992', 'The selected asset is hidden and a work order cannot be created.') + '\n\n' + this.r('FLTL_00999', 'Do you wish to "Un-Hide" the asset?'), [
{ key: 'unhide', text: this.r('FLTL_03136', 'Unhide') },
{ key: 'unhide', text: this.r('FLTL_03378', 'Yes') },
{ key: 'cancel', text: this.r('FLTL_00502', 'Cancel Work Order') }
]);
if (next.result !== 'unhide') {
@@ -672,10 +721,142 @@ export default class AddWorkOrder extends OptionBase {
if (next !== 'create') {
return false;
}
if ((item.Status == 100 || item.StatusType == 100)
&& (item.AssetCustomStatus == 10 || item.AssetCustomStatusType == 10)) {
let msgdiv = createElement('div', div => {
div.style.minWidth = "300px";
div.style.marginLeft = "50px";
div.style.fontSize = "14px";
let msghtml = this.r('FLTL_03591', 'Asset Availability is set to');
msghtml += '&nbsp;<b style="color:#3d7892;">' + item.AssetCustomStatusName + '</b>';
msghtml += '<br/><br/>' + this.r('FLTL_03592', 'Would you like to update?');
div.innerHTML = msghtml;
}
);
const nextassetstatus = await showConfirm(title, msgdiv, [
{ key: 'yes', text: this.r('FLTL_03378', 'Yes') },
{ key: 'no', text: this.r('FLTL_01978', 'No') }
]);
if (nextassetstatus.result !== 'no') {
return false;
}
}
}
}
const el = this._var.el;
if (item.Status > 0 && item.CustomerId > 0 && item.StatusMessage && allowCommunicate && !commReadOnly) {
const next = await new Promise(resolve => {
let addButton;
const chkSend = createCheckbox({
label: this.r('FLTL_02700', 'Send Update To Customer'),
onchange: () => {
const chk = chkSend.querySelector('input');
el.chkLink.querySelector('input').disabled = !chk.checked;
el.txtMessage.disabled = !chk.checked;
el.txtPhoneNumber.disabled = !chk.checked;
dropMSGVariables.disabled = !chk.checked;
addButton.disabled = !chk.checked;
}
});
el.chkSend = chkSend;
chkSend.style.paddingLeft = '0px';
const chkLink = createCheckbox({
label: this.r('FLTL_01580', 'Include Status Link')
});
el.chkLink = chkLink;
chkLink.style.paddingLeft = '0px';
el.txtMessage = textMessage;
const iconmobile = createIcon('fa-solid', 'mobile-alt');
const sPopup = new Popup({
title: this.r('FLTL_02824', 'Status Change') + " - " + item.StatusName,
content: createElement('div', 'wo-send-status-msg',
createElement('label'),
chkSend,
createElement('div', div => {
div.style.textAlign = 'right';
div.style.paddingRight = '5px';
div.appendChild(iconmobile);
}),
createElement('input', input => {
input.type = 'text';
el.txtPhoneNumber = input;
}),
createElement('label'),
chkLink,
createElement('label', label => {
label.innerText = this.r('FLTL_01912', 'Message:');
label.style.textAlign = 'right';
label.style.paddingRight = '5px';
}),
createElement('div', div => {
div.style.display = 'flex';
div.style.alignItems = 'center';
div.appendChild(dropMSGVariablesElement);
addButton = createElement('input', input => {
input.type = 'button';
input.style.marginLeft = "10px";
input.style.height = "24px";
input.value = this.r('FLTL_00083', 'Add');
})
addButton.addEventListener('click', () => {
const text = dropMSGVariables.selected?.Name;
if (!text) return;
textMessage.focus();
const startPos = textMessage.selectionStart;
const endPos = textMessage.selectionEnd;
textMessage.value = textMessage.value.substring(0, startPos) + text + textMessage.value.substring(endPos, textMessage.value.length);
textMessage.selectionStart = textMessage.selectionEnd = startPos + text.length;
});
div.appendChild(addButton);
}),
createElement('label'),
textMessage
),
resolve,
buttons: [
{ key: 'ok', text: this.r('FLTL_02582', 'Save Work Order and Send'), trigger: () => resolve('ok') },
{ key: 'cancel', text: this.r('FLTL_00499', 'Cancel'), trigger: () => resolve('cancel') }
]
});
var names = "";
for (var i = 0; i < item.Contacts.length; i++) {
var c = item.Contacts[i];
if (c.OptOut || c.OptOut_BC) continue;
var mp = $.trim(c.MobilePhone);
var email = $.trim(c.Email);
if ((c.ContactPreference == "0" && (checkPhoneNumber(mp))
|| (c.ContactPreference == "1" && isEmail(email)))) {
if (names == "")
names = c.Name;
else
names += ";" + c.Name;
}
}
el.txtPhoneNumber.value = names;
el.chkSend.querySelector('input').checked = item.StatusAutoText;
el.chkSend.querySelector('input').dispatchEvent(new Event('change'));
el.txtMessage.value = item.StatusMessage;
dropMSGVariables.select(dropMSGVariables.source[0].Name);
sPopup.show().then(mask => {
});
});
if (next !== 'ok') {
return false;
}
}
if (typeof this.onSave === 'function') {
this.onSave(item, this._var.el.dropStatus.selected);
const sendStatusInfo = (item.Status > 0 && item.CustomerId > 0 && item.StatusMessage && el.chkSend) ? {
SendUpdateToCustomer: el.chkSend.querySelector('input').checked,
IncludeStatusLink: el.chkLink.querySelector('input').checked,
PhoneEmails: el.txtPhoneNumber.value,
Comment: el.txtMessage.value,
} : null;
this.onSave(item, sendStatusInfo);
}
} finally {
popup.loading = false;
@@ -692,6 +873,7 @@ export default class AddWorkOrder extends OptionBase {
if (typeof option.requestWorkOrderParams === 'function') {
popup.loading = true;
const data = await option.requestWorkOrderParams()
dropAssetStatus.source = data.AssetAvailabilities;
if (!isNaN(data.AssetId) && data.AssetId > 0 && typeof option.requestAssetInfo === 'function') {
const it = await option.requestAssetInfo(data.AssetId);
if (it != null) {
@@ -712,6 +894,7 @@ export default class AddWorkOrder extends OptionBase {
dropAssignedTo.source = [{ IID: '', DisplayName: '' }, ...data];
// dropStatus.onSelected(dropStatus.selected);
}
dropAssetStatus.select(it.CustomStatus);
}
}
popup.loading = false;