1896 lines
52 KiB
Vue
1896 lines
52 KiB
Vue
<template>
|
|
<div>
|
|
<gz-report-selector ref="reportSelector"></gz-report-selector>
|
|
<div v-if="formState.ready">
|
|
<gz-error :error-box-message="formState.errorBoxMessage"></gz-error>
|
|
<gz-alert :alert-message="obj.alertViz"></gz-alert>
|
|
<!-- SERVERERROR:{{ formState.serverError }} -->
|
|
<v-form ref="form">
|
|
<GzWoHeader
|
|
v-model="obj"
|
|
:form-key="formCustomTemplateKey"
|
|
:readonly="formState.readOnly"
|
|
:pvm="this"
|
|
data-cy="woHeader"
|
|
@change="setDirty()"
|
|
/>
|
|
<GzWoItems
|
|
v-model="obj"
|
|
:form-key="formCustomTemplateKey"
|
|
:readonly="formState.readOnly"
|
|
:pvm="this"
|
|
data-cy="woItems"
|
|
@change="setDirty()"
|
|
class="mt-16"
|
|
/>
|
|
</v-form>
|
|
</div>
|
|
<template v-if="!formState.ready">
|
|
<v-progress-circular
|
|
indeterminate
|
|
color="primary"
|
|
:size="60"
|
|
></v-progress-circular>
|
|
</template>
|
|
</div>
|
|
<!-- :reset-selections="resetSelections" -->
|
|
</template>
|
|
|
|
<script>
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/* Xeslint-disable */
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
import GzWoHeader from "../components/work-order-header.vue";
|
|
import GzWoItems from "../components/work-order-items.vue";
|
|
|
|
const FORM_KEY = "workorder-edit";
|
|
const API_BASE_URL = "workorder/";
|
|
const FORM_CUSTOM_TEMPLATE_KEY = "WorkOrder"; //<-- Should always be CoreBizObject AyaType name here where possible
|
|
|
|
export default {
|
|
components: {
|
|
GzWoHeader,
|
|
GzWoItems
|
|
},
|
|
async created() {
|
|
const vm = this;
|
|
|
|
try {
|
|
await initForm(vm);
|
|
|
|
//set base rights
|
|
vm.rights = window.$gz.role.getRights(window.$gz.type.WorkOrder);
|
|
window.$gz.eventBus.$on("menu-click", clickHandler);
|
|
|
|
//id 0 means create a new record don't load one
|
|
if (vm.$route.params.recordid != 0) {
|
|
//is there already an obj from a prior operation?
|
|
if (this.$route.params.obj) {
|
|
//yes, no need to fetch it
|
|
this.obj = this.$route.params.obj;
|
|
|
|
window.$gz.form.setFormState({
|
|
vm: vm,
|
|
loading: false
|
|
});
|
|
} else {
|
|
await vm.getDataFromApi(vm.$route.params.recordid); //let getdata handle loading
|
|
}
|
|
} else {
|
|
window.$gz.form.setFormState({
|
|
vm: vm,
|
|
loading: false
|
|
});
|
|
}
|
|
window.$gz.form.setFormState({
|
|
vm: vm,
|
|
dirty: false,
|
|
valid: true
|
|
});
|
|
|
|
//update which areas are available to user and force generate menu
|
|
updateRights(vm, true);
|
|
} catch (error) {
|
|
window.$gz.errorHandler.handleFormError(error, vm);
|
|
} finally {
|
|
vm.formState.ready = true;
|
|
}
|
|
},
|
|
async beforeRouteLeave(to, from, next) {
|
|
if (!this.formState.dirty || JUST_DELETED) {
|
|
next();
|
|
return;
|
|
}
|
|
if ((await window.$gz.dialog.confirmLeaveUnsaved()) === true) {
|
|
next();
|
|
} else {
|
|
next(false);
|
|
}
|
|
},
|
|
beforeDestroy() {
|
|
window.$gz.eventBus.$off("menu-click", clickHandler);
|
|
},
|
|
data() {
|
|
return {
|
|
formCustomTemplateKey: FORM_CUSTOM_TEMPLATE_KEY,
|
|
obj: {
|
|
id: 0,
|
|
concurrency: 0,
|
|
serial: 0,
|
|
notes: null,
|
|
wiki: null,
|
|
customFields: "{}",
|
|
tags: [],
|
|
customerId: null,
|
|
projectId: null,
|
|
contractId: null,
|
|
internalReferenceNumber: null,
|
|
customerReferenceNumber: null,
|
|
customerContactName: null,
|
|
fromQuoteId: null,
|
|
fromPMId: null,
|
|
fromCSRId: null,
|
|
serviceDate: null,
|
|
completeByDate: null,
|
|
durationToCompleted: "00:00:00",
|
|
invoiceNumber: null,
|
|
onsite: true,
|
|
customerSignature: null,
|
|
customerSignatureName: null,
|
|
customerSignatureCaptured: null,
|
|
techSignature: null,
|
|
techSignatureName: null,
|
|
techSignatureCaptured: null,
|
|
postAddress: null,
|
|
postCity: null,
|
|
postRegion: null,
|
|
postCountry: null,
|
|
postCode: null,
|
|
address: null,
|
|
city: null,
|
|
region: null,
|
|
country: null,
|
|
latitude: null,
|
|
longitude: null,
|
|
items: [],
|
|
states: [],
|
|
isDirty: true,
|
|
isLockedAtServer: false,
|
|
alertViz: null
|
|
},
|
|
formState: {
|
|
ready: false,
|
|
dirty: false,
|
|
valid: true,
|
|
readOnly: false,
|
|
loading: true,
|
|
errorBoxMessage: null,
|
|
appError: null,
|
|
serverError: {}
|
|
},
|
|
rights: window.$gz.role.defaultRightsObject(), //overall workorder rights, supersedes subrights
|
|
ayaType: window.$gz.type.WorkOrder,
|
|
currencyName: window.$gz.locale.getCurrencyName(),
|
|
timeZoneName: window.$gz.locale.getResolvedTimeZoneName(),
|
|
languageName: window.$gz.locale.getResolvedLanguage(),
|
|
hour12: window.$gz.locale.getHour12(),
|
|
// resetSelections: false,
|
|
selectLists: {
|
|
wostatus: [],
|
|
allowedwostatus: [],
|
|
woItemPriorities: [],
|
|
woItemStatus: [],
|
|
woItemTaskCompletionTypes: []
|
|
},
|
|
subRights: {
|
|
items: {
|
|
visible: true,
|
|
create: true,
|
|
change: true,
|
|
delete: true
|
|
},
|
|
states: {
|
|
visible: true,
|
|
create: true, //no change, only create it's write only
|
|
delete: true
|
|
},
|
|
labors: {
|
|
visible: true,
|
|
create: true,
|
|
change: true,
|
|
delete: true
|
|
},
|
|
parts: {
|
|
visible: true,
|
|
create: true,
|
|
change: true,
|
|
delete: true
|
|
},
|
|
partRequests: {
|
|
visible: true,
|
|
create: true,
|
|
change: true,
|
|
delete: true
|
|
},
|
|
expenses: {
|
|
visible: true,
|
|
create: true,
|
|
change: true,
|
|
delete: true
|
|
},
|
|
loans: {
|
|
visible: true,
|
|
create: true,
|
|
change: true,
|
|
delete: true
|
|
},
|
|
scheduledUsers: {
|
|
visible: true,
|
|
create: true,
|
|
change: true,
|
|
delete: true
|
|
},
|
|
tasks: {
|
|
visible: true,
|
|
create: true,
|
|
change: true,
|
|
delete: true
|
|
},
|
|
travels: {
|
|
visible: true,
|
|
create: true,
|
|
change: true,
|
|
delete: true
|
|
},
|
|
units: {
|
|
visible: true,
|
|
create: true,
|
|
change: true,
|
|
delete: true
|
|
},
|
|
outsideServices: {
|
|
visible: true,
|
|
create: true,
|
|
change: true,
|
|
delete: true
|
|
}
|
|
},
|
|
saveResult: {
|
|
fatal: false, //fatal error, further save is pointless, bail early and report
|
|
errors: null //contains error objects from save
|
|
},
|
|
lastGetContractId: -1, //note: -1 so that a new record updates
|
|
lastGetCustomerId: -1
|
|
};
|
|
},
|
|
//WATCHERS
|
|
watch: {
|
|
formState: {
|
|
handler: function(val) {
|
|
//,oldval is available here too if necessary
|
|
if (this.formState.loading) {
|
|
return;
|
|
}
|
|
|
|
//enable / disable save button
|
|
if (val.dirty && val.valid) {
|
|
window.$gz.eventBus.$emit("menu-enable-item", FORM_KEY + ":save");
|
|
} else {
|
|
window.$gz.eventBus.$emit("menu-disable-item", FORM_KEY + ":save");
|
|
}
|
|
|
|
//enable / disable duplicate / new button
|
|
if (!val.dirty && val.valid && !val.readOnly) {
|
|
window.$gz.eventBus.$emit(
|
|
"menu-enable-item",
|
|
FORM_KEY + ":duplicate"
|
|
);
|
|
window.$gz.eventBus.$emit("menu-enable-item", FORM_KEY + ":new");
|
|
} else {
|
|
window.$gz.eventBus.$emit(
|
|
"menu-disable-item",
|
|
FORM_KEY + ":duplicate"
|
|
);
|
|
window.$gz.eventBus.$emit("menu-disable-item", FORM_KEY + ":new");
|
|
}
|
|
},
|
|
deep: true
|
|
}
|
|
},
|
|
computed: {
|
|
currentState() {
|
|
//return actual status object from top level shell based on current state
|
|
//if state is unknown then it should return a placeholder dummy state showing an error condition or empty I guess
|
|
|
|
if (this.obj.states != null && this.obj.states.length > 0) {
|
|
//find it in the status collection
|
|
//and return here
|
|
const laststate = this.obj.states[this.obj.states.length - 1];
|
|
const found = this.selectLists.wostatus.find(
|
|
z => z.id == laststate.workOrderStatusId
|
|
);
|
|
if (found) {
|
|
return found;
|
|
}
|
|
}
|
|
//default
|
|
return {
|
|
id: 0,
|
|
name: "-",
|
|
active: true,
|
|
color: "#ffffff", //invisible
|
|
completed: false,
|
|
locked: false
|
|
};
|
|
}
|
|
},
|
|
methods: {
|
|
setDirty: function() {
|
|
this.formState.dirty = true;
|
|
},
|
|
canSave: function() {
|
|
return this.formState.valid && this.formState.dirty;
|
|
},
|
|
canDuplicate: function() {
|
|
return this.formState.valid && !this.formState.dirty;
|
|
},
|
|
ayaTypes: function() {
|
|
return window.$gz.type;
|
|
},
|
|
form() {
|
|
return window.$gz.form;
|
|
},
|
|
fieldValueChanged(ref) {
|
|
if (this.formState.ready && !this.formState.loading) {
|
|
window.$gz.form.fieldValueChanged(this, ref);
|
|
}
|
|
},
|
|
async getDataFromApi(recordId) {
|
|
const vm = this;
|
|
window.$gz.form.setFormState({
|
|
vm: vm,
|
|
loading: true
|
|
});
|
|
if (!recordId) {
|
|
throw new Error(FORM_KEY + "::getDataFromApi -> Missing recordID!");
|
|
}
|
|
const url = API_BASE_URL + recordId;
|
|
try {
|
|
window.$gz.form.deleteAllErrorBoxErrors(vm);
|
|
|
|
const res = await window.$gz.api.get(url);
|
|
|
|
if (res.error) {
|
|
//Not found?
|
|
if (res.error.code == "2010") {
|
|
window.$gz.form.handleObjectNotFound(vm);
|
|
}
|
|
vm.formState.serverError = res.error;
|
|
window.$gz.form.setErrorBoxErrors(vm);
|
|
} else {
|
|
vm.obj = res.data;
|
|
|
|
vm.lastGetContractId = vm.obj.contractId; //preserve for triggering full update if something changes it later
|
|
vm.lastGetCustomerId = vm.obj.customerId; //preserve for triggering full update if something changes it later
|
|
|
|
//assign opening UID's
|
|
vm.obj.items.forEach((z, index) => {
|
|
z.uid = index;
|
|
z.expenses.forEach((x, index) => (x.uid = index));
|
|
z.labors.forEach((x, index) => (x.uid = index));
|
|
z.loans.forEach((x, index) => (x.uid = index));
|
|
z.parts.forEach((x, index) => (x.uid = index));
|
|
z.partRequests.forEach((x, index) => (x.uid = index));
|
|
z.scheduledUsers.forEach((x, index) => (x.uid = index));
|
|
z.tasks.forEach((x, index) => (x.uid = index));
|
|
z.travels.forEach((x, index) => (x.uid = index));
|
|
z.units.forEach((x, index) => (x.uid = index));
|
|
z.outsideServices.forEach((x, index) => (x.uid = index));
|
|
});
|
|
|
|
//modify the menu as necessary
|
|
generateMenu(vm);
|
|
//update which areas are available to user
|
|
updateRights(vm);
|
|
|
|
//Update the form status
|
|
window.$gz.form.setFormState({
|
|
vm: vm,
|
|
dirty: false,
|
|
valid: true,
|
|
loading: false
|
|
});
|
|
}
|
|
} catch (error) {
|
|
window.$gz.errorHandler.handleFormError(error, vm);
|
|
} finally {
|
|
window.$gz.form.setFormState({
|
|
vm: vm,
|
|
loading: false
|
|
});
|
|
}
|
|
},
|
|
// async submitNewContract() {
|
|
// //save new contract route, this only ever gets called from a clean wo with no dirty edits so just save the contract and reset the wo from the result
|
|
// let res = await window.$gz.api.post(
|
|
// `${API_BASE_URL}set-contract/${this.obj.id}`,
|
|
// { newContractId: this.obj.contractId }
|
|
// );
|
|
// if (res.error) {
|
|
// this.formState.serverError = res.error;
|
|
// window.$gz.form.setErrorBoxErrors(this);
|
|
// } else {
|
|
// this.obj = res.data;
|
|
// window.$gz.form.setFormState({
|
|
// vm: this,
|
|
// dirty: false,
|
|
// valid: true
|
|
// });
|
|
// }
|
|
// },
|
|
async submit() {
|
|
const vm = this;
|
|
if (vm.canSave == false) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
window.$gz.form.setFormState({
|
|
vm: vm,
|
|
loading: true
|
|
});
|
|
|
|
//clear any errors vm might be around from previous submit
|
|
window.$gz.form.deleteAllErrorBoxErrors(vm);
|
|
|
|
//#######################################################
|
|
// ███████╗ █████╗ ██╗ ██╗███████╗
|
|
// ██╔════╝██╔══██╗██║ ██║██╔════╝
|
|
// ███████╗███████║██║ ██║█████╗
|
|
// ╚════██║██╔══██║╚██╗ ██╔╝██╔══╝
|
|
// ███████║██║ ██║ ╚████╔╝ ███████╗
|
|
// ╚══════╝╚═╝ ╚═╝ ╚═══╝ ╚══════╝
|
|
//########################################################
|
|
|
|
const isPost = vm.obj.id == 0;
|
|
|
|
//some ops require a full refresh like ones that trigger contract changes
|
|
let forceFullRefresh = false;
|
|
|
|
//reset error object
|
|
this.saveResult.fatal = false;
|
|
this.saveResult.errors = null;
|
|
|
|
//UNSAVED HEADER MUST BE FIRST
|
|
//(otherwise there's nothing to hang the other things off of)
|
|
let headerSaved = false;
|
|
if (this.obj.concurrency == 0) {
|
|
await saveHeader(vm);
|
|
headerSaved = true;
|
|
}
|
|
|
|
//LOCKED? State must be saved first then (assuming it unlocks)
|
|
let stateSaved = false;
|
|
if (this.obj.isLockedAtServer) {
|
|
await saveState(vm);
|
|
if (!this.saveResult.fatal) {
|
|
stateSaved = true;
|
|
//update which areas are available to user
|
|
//which may have changed due to state being saved (saveState sets the current islocked value)
|
|
updateRights(vm);
|
|
}
|
|
}
|
|
|
|
//HEADER
|
|
if (!this.saveResult.fatal && !headerSaved) {
|
|
await saveHeader(vm);
|
|
}
|
|
|
|
//WOITEMS
|
|
if (!this.saveResult.fatal) {
|
|
//first sort items into sequence order so that the errors line up with the display
|
|
this.obj.items.sort((a, b) => a.sequence - b.sequence);
|
|
//This saves all bottom level collections as well
|
|
await saveItems(vm);
|
|
}
|
|
|
|
//### STATE last normally
|
|
//in case it locks or is completed
|
|
if (!this.saveResult.fatal && !stateSaved) {
|
|
await saveState(vm);
|
|
if (!this.saveResult.fatal) {
|
|
updateRights(vm);
|
|
}
|
|
}
|
|
|
|
//## ALL PARTIAL UPDATES COMPLETED
|
|
//handle errors
|
|
if (this.saveResult.errors != null) {
|
|
//# FAIL ROUTE
|
|
const processedErrors = formErrorFromSaveResult(this);
|
|
vm.formState.serverError = processedErrors; //this is the bit that triggers field and row errors to display as they pickup from this setting
|
|
window.$gz.form.setErrorBoxErrors(vm); //set generalerror errors in error box at top, not related to form field errors which happen alternatively based on formState.serverError which is confusing
|
|
} else {
|
|
//# SUCCESS ROUTE
|
|
if (isPost) {
|
|
//nav to id'd url
|
|
//note that an isPost will never be here if there is a fatal error so this is safe to do
|
|
this.$router.push({
|
|
name: "workorder-edit",
|
|
params: {
|
|
recordid: vm.obj.id
|
|
// ,obj: vm.obj // Do NOT Pass data object to new form as normal because for a workorder, it's not a full and complete record at this end
|
|
}
|
|
});
|
|
} else {
|
|
//check if full refresh is necessary
|
|
|
|
if (
|
|
vm.obj.contractId != vm.lastGetContractId ||
|
|
vm.obj.customerId != vm.lastGetCustomerId
|
|
) {
|
|
//there may be others which is why I'm doing it this way with the extra variable
|
|
forceFullRefresh = true;
|
|
}
|
|
|
|
if (forceFullRefresh) {
|
|
await vm.getDataFromApi(vm.$route.params.recordid);
|
|
} else {
|
|
window.$gz.form.setFormState({
|
|
vm: vm,
|
|
dirty: false,
|
|
valid: true
|
|
});
|
|
}
|
|
}
|
|
}
|
|
} catch (ex) {
|
|
window.$gz.errorHandler.handleFormError(ex, vm);
|
|
} finally {
|
|
window.$gz.form.setFormState({
|
|
vm: vm,
|
|
loading: false
|
|
});
|
|
}
|
|
},
|
|
async remove() {
|
|
let vm = this;
|
|
try {
|
|
let dialogResult = await window.$gz.dialog.confirmDelete();
|
|
if (dialogResult != true) {
|
|
return;
|
|
}
|
|
|
|
//do the delete
|
|
window.$gz.form.setFormState({
|
|
vm: vm,
|
|
loading: true
|
|
});
|
|
//No need to delete a new record, just abandon it...
|
|
if (vm.$route.params.recordid == 0) {
|
|
//this should not get offered for delete but to be safe and clear just in case:
|
|
JUST_DELETED = true;
|
|
// navigate backwards
|
|
vm.$router.go(-1);
|
|
} else {
|
|
let url = API_BASE_URL + vm.$route.params.recordid;
|
|
|
|
window.$gz.form.deleteAllErrorBoxErrors(vm);
|
|
let res = await window.$gz.api.remove(url);
|
|
if (res.error) {
|
|
vm.formState.serverError = res.error;
|
|
window.$gz.form.setErrorBoxErrors(vm);
|
|
} else {
|
|
//workaround to prevent warning about leaving dirty record
|
|
//For some reason I couldn't just reset isdirty in formstate
|
|
JUST_DELETED = true;
|
|
// navigate backwards
|
|
vm.$router.go(-1);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
//Update the form status
|
|
window.$gz.form.setFormState({
|
|
vm: vm,
|
|
loading: false
|
|
});
|
|
window.$gz.errorHandler.handleFormError(error, vm);
|
|
}
|
|
},
|
|
async duplicate() {
|
|
let vm = this;
|
|
if (!vm.canDuplicate || vm.$route.params.recordid == 0) {
|
|
return;
|
|
}
|
|
window.$gz.form.setFormState({
|
|
vm: vm,
|
|
loading: true
|
|
});
|
|
let url = API_BASE_URL + "duplicate/" + vm.$route.params.recordid;
|
|
|
|
try {
|
|
window.$gz.form.deleteAllErrorBoxErrors(vm);
|
|
let res = await window.$gz.api.upsert(url);
|
|
if (res.error) {
|
|
vm.formState.serverError = res.error;
|
|
window.$gz.form.setErrorBoxErrors(vm);
|
|
} else {
|
|
//Navigate to new record
|
|
this.$router.push({
|
|
name: "workorder-edit",
|
|
params: {
|
|
recordid: res.data.id,
|
|
obj: res.data // Pass data object to new form
|
|
}
|
|
});
|
|
}
|
|
} catch (ex) {
|
|
window.$gz.errorHandler.handleFormError(ex, vm);
|
|
} finally {
|
|
window.$gz.form.setFormState({
|
|
vm: vm,
|
|
loading: false
|
|
});
|
|
}
|
|
}
|
|
//end methods
|
|
}
|
|
};
|
|
|
|
//########################################## SAVE METHODS ##############################################
|
|
|
|
/////////////////////////////
|
|
// HEADER
|
|
//
|
|
async function saveHeader(vm) {
|
|
if (!vm.obj.isDirty) {
|
|
return;
|
|
}
|
|
const isPost = vm.obj.id == 0;
|
|
|
|
//the purpose of this is to remove the child collections so only the header itself is submitted
|
|
//this was the cleanest way I could find to accomplish this
|
|
//https://stackoverflow.com/a/58206483/8939
|
|
const { items: removedKey1, states: removedKey2, ...headerOnly } = vm.obj;
|
|
|
|
//remove *Viz keys so they don't generate traffic
|
|
headerOnly.alertViz = undefined;
|
|
headerOnly.projectViz = undefined;
|
|
headerOnly.contractViz = undefined;
|
|
headerOnly.customerViz = undefined;
|
|
|
|
let res = await window.$gz.api.upsert(`${API_BASE_URL}`, headerOnly);
|
|
if (res.error) {
|
|
handleSaveError(vm, { fatal: true, error: res.error });
|
|
} else {
|
|
//update any server changed fields
|
|
vm.obj.concurrency = res.data.concurrency;
|
|
vm.obj.isDirty = false;
|
|
|
|
vm.obj.customerId = res.data.customerId;
|
|
vm.obj.contractId = res.data.contractId; //may or may not have changed at server, this will ensure entire ui gets updated if it has as all prices may have changed and other stuff
|
|
|
|
//repopulate *viz fields from return value
|
|
vm.obj.alertViz = res.data.alertViz;
|
|
vm.obj.projectViz = res.data.projectViz;
|
|
vm.obj.contractViz = res.data.contractViz;
|
|
vm.obj.customerViz = res.data.customerViz;
|
|
|
|
if (isPost) {
|
|
vm.obj.id = res.data.id;
|
|
vm.obj.serial = res.data.serial;
|
|
//walk all unsaved direct children and set the workorder id so they can save
|
|
vm.obj.states.forEach(z => (z.workOrderId = vm.obj.id));
|
|
vm.obj.items.forEach(z => (z.workOrderId = vm.obj.id));
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////
|
|
// STATES
|
|
//
|
|
async function saveState(vm) {
|
|
//CHANGED?
|
|
let totalItems = vm.obj.states.length;
|
|
if (totalItems == 0) {
|
|
return;
|
|
}
|
|
for (let i = 0; i < totalItems; i++) {
|
|
let o = vm.obj.states[i];
|
|
if (o.concurrency == null) {
|
|
//it's new so save it
|
|
let res = await window.$gz.api.upsert(`${API_BASE_URL}states`, o);
|
|
if (res.error) {
|
|
handleSaveError(vm, { error: res.error });
|
|
} else {
|
|
vm.obj.states[i] = res.data;
|
|
|
|
//set locked status of entire wo now
|
|
vm.obj.isLockedAtServer = vm.currentState.locked;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////
|
|
// ITEMS
|
|
//
|
|
async function deleteItems(vm) {
|
|
//walk the array backwards as items may or may not be spliced out
|
|
for (var i = vm.obj.items.length - 1; i >= 0; i--) {
|
|
const d = vm.obj.items[i];
|
|
if (!d.deleted) {
|
|
continue;
|
|
}
|
|
let res = await window.$gz.api.remove(`${API_BASE_URL}items/${d.id}`);
|
|
if (res.error) {
|
|
handleSaveError(vm, { error: res.error, itemUid: d.uid });
|
|
} else {
|
|
vm.obj.items.splice(i, 1);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
async function saveItems(vm) {
|
|
//DELETE FLAGGED WOITEMS FIRST
|
|
await deleteItems(vm);
|
|
if (vm.saveResult.fatal) {
|
|
return;
|
|
}
|
|
|
|
//SAVE WOITEMS
|
|
for (let i = 0; i < vm.obj.items.length; i++) {
|
|
//get copy of item without child collections for independant submit
|
|
const {
|
|
expenses: removedKey1,
|
|
labors: removedKey2,
|
|
loans: removedKey3,
|
|
parts: removedKey4,
|
|
partRequests: removedKey5,
|
|
scheduledUsers: removedKey6,
|
|
tasks: removedKey7,
|
|
travels: removedKey8,
|
|
units: removedKey9,
|
|
outsideServices: removedKey10,
|
|
...o
|
|
} = vm.obj.items[i];
|
|
if (o.isDirty) {
|
|
const isPost = o.id == 0;
|
|
let res = await window.$gz.api.upsert(`${API_BASE_URL}items`, o);
|
|
if (res.error) {
|
|
handleSaveError(vm, { error: res.error, itemUid: o.uid });
|
|
if (isPost) {
|
|
//a post error precludes further operations on this item below
|
|
//however, an update error doesn't necessarily because it's still a existing workorder item
|
|
//so it's children can probably be updated and we want that
|
|
continue;
|
|
}
|
|
} else {
|
|
//no error so update any server changed fields
|
|
//put fields
|
|
vm.obj.items[i].concurrency = res.data.concurrency;
|
|
vm.obj.items[i].isDirty = false;
|
|
|
|
//fields to update if post
|
|
if (isPost) {
|
|
vm.obj.items[i].id = res.data.id;
|
|
vm.obj.items[i].workorderId = res.data.workorderId;
|
|
//walk all unsaved children and set the workorder id so they can save
|
|
vm.obj.items[i].expenses.forEach(
|
|
z => (z.workorderItemId = vm.obj.items[i].id)
|
|
);
|
|
|
|
vm.obj.items[i].scheduledUsers.forEach(
|
|
z => (z.workorderItemId = vm.obj.items[i].id)
|
|
);
|
|
|
|
vm.obj.items[i].labors.forEach(
|
|
z => (z.workorderItemId = vm.obj.items[i].id)
|
|
);
|
|
|
|
vm.obj.items[i].travels.forEach(
|
|
z => (z.workorderItemId = vm.obj.items[i].id)
|
|
);
|
|
|
|
//todo: other grandchildren
|
|
}
|
|
}
|
|
} else {
|
|
// Item not dirty skipping save
|
|
}
|
|
//------
|
|
//save grandchildren
|
|
if (!vm.saveResult.fatal) {
|
|
await saveExpenses(vm, i);
|
|
}
|
|
if (!vm.saveResult.fatal) {
|
|
await saveScheduledUsers(vm, i);
|
|
}
|
|
if (!vm.saveResult.fatal) {
|
|
await saveLabors(vm, i);
|
|
}
|
|
if (!vm.saveResult.fatal) {
|
|
await saveTravels(vm, i);
|
|
}
|
|
//todo: other grandchildren
|
|
//------
|
|
}
|
|
}
|
|
//####################################################################################
|
|
//## GRANDCHILDREN
|
|
|
|
/////////////////////////////
|
|
// EXPENSES
|
|
//
|
|
async function deleteExpenses(vm, woItemIndex) {
|
|
//walk the array backwards as items may be spliced out
|
|
for (var i = vm.obj.items[woItemIndex].expenses.length - 1; i >= 0; i--) {
|
|
const d = vm.obj.items[woItemIndex].expenses[i];
|
|
if (!d.deleted) {
|
|
continue;
|
|
}
|
|
let res = await window.$gz.api.remove(
|
|
`${API_BASE_URL}items/expenses/${d.id}`
|
|
);
|
|
if (res.error) {
|
|
handleSaveError(vm, {
|
|
error: res.error,
|
|
itemUid: vm.obj.items[woItemIndex].uid,
|
|
childKey: "expenses",
|
|
childUid: d.uid
|
|
});
|
|
} else {
|
|
vm.obj.items[woItemIndex].expenses.splice(i, 1);
|
|
}
|
|
}
|
|
|
|
//----
|
|
return;
|
|
}
|
|
|
|
async function saveExpenses(vm, woItemIndex) {
|
|
//DELETE FLAGGED ITEMS FIRST
|
|
await deleteExpenses(vm, woItemIndex);
|
|
if (vm.saveResult.fatal) {
|
|
return;
|
|
}
|
|
|
|
for (let i = 0; i < vm.obj.items[woItemIndex].expenses.length; i++) {
|
|
let o = vm.obj.items[woItemIndex].expenses[i];
|
|
if (o.isDirty) {
|
|
const isPost = o.id == 0;
|
|
let res = await window.$gz.api.upsert(`${API_BASE_URL}items/expenses`, o);
|
|
if (res.error) {
|
|
handleSaveError(vm, {
|
|
error: res.error,
|
|
itemUid: vm.obj.items[woItemIndex].uid,
|
|
childKey: "expenses",
|
|
childUid: o.uid
|
|
});
|
|
} else {
|
|
//Server will update fields on put or post for most workorder graph objecs so need to update entire object here
|
|
vm.obj.items[woItemIndex].expenses.splice(i, 1, res.data); //vue needs the splice rather than just setting the value in order to trigger reactivity or else the UI won't update
|
|
}
|
|
}
|
|
}
|
|
return; //made it
|
|
}
|
|
|
|
/////////////////////////////
|
|
// SCHEDULED USERS
|
|
//
|
|
async function saveScheduledUsers(vm, woItemIndex) {
|
|
//DELETE FLAGGED ITEMS FIRST
|
|
await deleteScheduledUsers(vm, woItemIndex);
|
|
if (vm.saveResult.fatal) {
|
|
return;
|
|
}
|
|
|
|
for (let i = 0; i < vm.obj.items[woItemIndex].scheduledUsers.length; i++) {
|
|
let o = vm.obj.items[woItemIndex].scheduledUsers[i];
|
|
if (o.isDirty) {
|
|
const isPost = o.id == 0;
|
|
let res = await window.$gz.api.upsert(
|
|
`${API_BASE_URL}items/scheduledusers`,
|
|
o
|
|
);
|
|
if (res.error) {
|
|
handleSaveError(vm, {
|
|
error: res.error,
|
|
itemUid: vm.obj.items[woItemIndex].uid,
|
|
childKey: "scheduledUsers",
|
|
childUid: o.uid
|
|
});
|
|
} else {
|
|
//Server will update fields on put or post for most workorder graph objecs so need to update entire object here
|
|
vm.obj.items[woItemIndex].scheduledUsers.splice(i, 1, res.data); //vue needs the splice rather than just setting the value in order to trigger reactivity or else the UI won't update
|
|
}
|
|
}
|
|
}
|
|
return; //made it
|
|
}
|
|
|
|
async function deleteScheduledUsers(vm, woItemIndex) {
|
|
//walk the array backwards as items may be spliced out
|
|
for (
|
|
var i = vm.obj.items[woItemIndex].scheduledUsers.length - 1;
|
|
i >= 0;
|
|
i--
|
|
) {
|
|
const d = vm.obj.items[woItemIndex].scheduledUsers[i];
|
|
if (!d.deleted) {
|
|
continue;
|
|
}
|
|
let res = await window.$gz.api.remove(
|
|
`${API_BASE_URL}items/scheduledusers/${d.id}`
|
|
);
|
|
if (res.error) {
|
|
handleSaveError(vm, {
|
|
error: res.error,
|
|
itemUid: vm.obj.items[woItemIndex].uid,
|
|
childKey: "scheduledUsers",
|
|
childUid: d.uid
|
|
});
|
|
} else {
|
|
vm.obj.items[woItemIndex].scheduledUsers.splice(i, 1);
|
|
}
|
|
}
|
|
|
|
//----
|
|
return;
|
|
}
|
|
|
|
/////////////////////////////
|
|
// LABOR
|
|
//
|
|
async function saveLabors(vm, woItemIndex) {
|
|
//DELETE FLAGGED ITEMS FIRST
|
|
await deleteLabors(vm, woItemIndex);
|
|
if (vm.saveResult.fatal) {
|
|
return;
|
|
}
|
|
|
|
for (let i = 0; i < vm.obj.items[woItemIndex].labors.length; i++) {
|
|
let o = vm.obj.items[woItemIndex].labors[i];
|
|
if (o.isDirty) {
|
|
const res = await window.$gz.api.upsert(`${API_BASE_URL}items/labors`, o);
|
|
if (res.error) {
|
|
handleSaveError(vm, {
|
|
error: res.error,
|
|
itemUid: vm.obj.items[woItemIndex].uid,
|
|
childKey: "labors",
|
|
childUid: o.uid
|
|
});
|
|
} else {
|
|
//Server will update fields on put or post for most workorder graph objecs so need to update entire object here
|
|
vm.obj.items[woItemIndex].labors.splice(i, 1, res.data); //vue needs the splice rather than just setting the value in order to trigger reactivity or else the UI won't update
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
async function deleteLabors(vm, woItemIndex) {
|
|
//walk the array backwards as items may be spliced out
|
|
for (var i = vm.obj.items[woItemIndex].labors.length - 1; i >= 0; i--) {
|
|
const d = vm.obj.items[woItemIndex].labors[i];
|
|
if (!d.deleted) {
|
|
continue;
|
|
}
|
|
let res = await window.$gz.api.remove(
|
|
`${API_BASE_URL}items/labors/${d.id}`
|
|
);
|
|
if (res.error) {
|
|
handleSaveError(vm, {
|
|
error: res.error,
|
|
itemUid: vm.obj.items[woItemIndex].uid,
|
|
childKey: "labors",
|
|
childUid: d.uid
|
|
});
|
|
} else {
|
|
vm.obj.items[woItemIndex].labors.splice(i, 1);
|
|
}
|
|
}
|
|
|
|
//----
|
|
return;
|
|
}
|
|
|
|
/////////////////////////////
|
|
// TRAVEL
|
|
//
|
|
async function saveTravels(vm, woItemIndex) {
|
|
//DELETE FLAGGED ITEMS FIRST
|
|
await deleteTravels(vm, woItemIndex);
|
|
if (vm.saveResult.fatal) {
|
|
return;
|
|
}
|
|
|
|
for (let i = 0; i < vm.obj.items[woItemIndex].travels.length; i++) {
|
|
let o = vm.obj.items[woItemIndex].travels[i];
|
|
if (o.isDirty) {
|
|
const res = await window.$gz.api.upsert(
|
|
`${API_BASE_URL}items/travels`,
|
|
o
|
|
);
|
|
if (res.error) {
|
|
handleSaveError(vm, {
|
|
error: res.error,
|
|
itemUid: vm.obj.items[woItemIndex].uid,
|
|
childKey: "travels",
|
|
childUid: o.uid
|
|
});
|
|
} else {
|
|
//Server will update fields on put or post for most workorder graph objecs so need to update entire object here
|
|
vm.obj.items[woItemIndex].travels.splice(i, 1, res.data); //vue needs the splice rather than just setting the value in order to trigger reactivity or else the UI won't update
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
async function deleteTravels(vm, woItemIndex) {
|
|
//walk the array backwards as items may be spliced out
|
|
for (var i = vm.obj.items[woItemIndex].travels.length - 1; i >= 0; i--) {
|
|
const d = vm.obj.items[woItemIndex].travels[i];
|
|
if (!d.deleted) {
|
|
continue;
|
|
}
|
|
let res = await window.$gz.api.remove(
|
|
`${API_BASE_URL}items/travels/${d.id}`
|
|
);
|
|
if (res.error) {
|
|
handleSaveError(vm, {
|
|
error: res.error,
|
|
itemUid: vm.obj.items[woItemIndex].uid,
|
|
childKey: "travels",
|
|
childUid: d.uid
|
|
});
|
|
} else {
|
|
vm.obj.items[woItemIndex].travels.splice(i, 1);
|
|
}
|
|
}
|
|
|
|
//----
|
|
return;
|
|
}
|
|
|
|
//todo: other grandchildren
|
|
|
|
//######################################### UTILITY METHODS ###########################################
|
|
|
|
function handleSaveError(vm, e) {
|
|
//### NOTE: Nothing using FATAL yet, but if necessary here is where it should be determined and set
|
|
|
|
if (vm.saveResult.errors == null) {
|
|
vm.saveResult.errors = [];
|
|
}
|
|
vm.saveResult.errors.push(e);
|
|
}
|
|
|
|
function errorTargetFromSaveResult(vm, r, t) {
|
|
//no particular target
|
|
//or not a woitem tree error (header)
|
|
if (r.itemUid == null || t == null || t == "generalerror") {
|
|
return t;
|
|
}
|
|
|
|
let woitemindex = vm.obj.items.findIndex(z => z.uid == r.itemUid);
|
|
if (woitemindex == -1) {
|
|
return null;
|
|
}
|
|
|
|
if (r.childKey == null || r.childUid == null) {
|
|
return `Items[${woitemindex}].${t}`;
|
|
}
|
|
|
|
let childindex = vm.obj.items[woitemindex][r.childKey].findIndex(
|
|
z => z.uid == r.childUid
|
|
);
|
|
if (childindex == -1) {
|
|
return null;
|
|
}
|
|
return `Items[${woitemindex}].${r.childKey}[${childindex}].${t}`;
|
|
}
|
|
|
|
function formErrorFromSaveResult(vm) {
|
|
/**
|
|
* convention:
|
|
* {
|
|
error: res.error,
|
|
itemUid: item.uid, //uid of item none means it's a header error
|
|
childKey: "scheduledUsers",
|
|
childUid: i //uid of child item will be located later as index
|
|
}
|
|
*
|
|
*/
|
|
//Note: re code 2200 hard coded here below -- if for some reason it becomes necessary there is the idea of making an envelope error to contain variety of errors,
|
|
//but so far I think they are all server errors (that go in the error box) or validation errors that work here
|
|
let ret = {
|
|
code: "2200",
|
|
details: [],
|
|
message: "ErrorAPI2200"
|
|
};
|
|
//console.log("formErrorFromSaveResult - input is:", vm.saveResult.errors);
|
|
vm.saveResult.errors.forEach(z => {
|
|
//console.log("z", z);
|
|
//{error:{code:xx,message:xx,target:xx},fatal:true}
|
|
if (z.error.details != null) {
|
|
z.error.details.forEach(x => {
|
|
let target = errorTargetFromSaveResult(vm, z, x.target);
|
|
if (target != null) {
|
|
ret.details.push({
|
|
message: x.message,
|
|
error: x.error,
|
|
target: target
|
|
});
|
|
}
|
|
});
|
|
} else {
|
|
//only one error so just return it
|
|
//console.log("single error path:", z.error);
|
|
ret = z.error;
|
|
//ret.details.push(z.error);
|
|
}
|
|
});
|
|
|
|
//console.log("Returning ret=:", ret);
|
|
return ret;
|
|
}
|
|
|
|
/////////////////////////////
|
|
//
|
|
//
|
|
function updateRights(vm, forceGenerateMenu) {
|
|
//determine rights to each which sections are hidden due to form customized out or rights / roles
|
|
const readOnlyBefore = vm.formState.readOnly;
|
|
if (vm.obj.isLockedAtServer) {
|
|
//locked is always read only (with state exception for sufficient roles)
|
|
vm.formState.readOnly = true;
|
|
} else {
|
|
//state may have changed so update readOnly
|
|
vm.formState.readOnly = !vm.rights.change;
|
|
}
|
|
|
|
if (readOnlyBefore != vm.formState.readOnly || forceGenerateMenu === true) {
|
|
generateMenu(vm);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////
|
|
//
|
|
//
|
|
async function clickHandler(menuItem) {
|
|
if (!menuItem) {
|
|
return;
|
|
}
|
|
let m = window.$gz.menu.parseMenuItem(menuItem);
|
|
if (m.owner == FORM_KEY && !m.disabled) {
|
|
switch (m.key) {
|
|
case "save":
|
|
m.vm.submit();
|
|
break;
|
|
case "delete":
|
|
m.vm.remove();
|
|
break;
|
|
case "new":
|
|
m.vm.$router.push({
|
|
name: "workorder-edit",
|
|
params: { recordid: 0, new: true }
|
|
});
|
|
break;
|
|
case "duplicate":
|
|
m.vm.duplicate();
|
|
break;
|
|
case "report":
|
|
if (m.id != null) {
|
|
//last report selected is in m.id
|
|
m.vm.$router.push({
|
|
name: "ay-report",
|
|
params: { recordid: m.id, ayatype: window.$gz.type.WorkOrder }
|
|
});
|
|
} else {
|
|
//general report selector chosen
|
|
|
|
let res = await m.vm.$refs.reportSelector.open({
|
|
AType: window.$gz.type.WorkOrder,
|
|
selectedRowIds: [m.vm.obj.id]
|
|
});
|
|
|
|
//if null for no selection
|
|
//just bail out
|
|
if (res == null) {
|
|
return;
|
|
}
|
|
//persist last report selected
|
|
window.$gz.form.setLastReport(FORM_KEY, res);
|
|
|
|
//Now open the report viewer...
|
|
m.vm.$router.push({
|
|
name: "ay-report",
|
|
params: { recordid: res.id, ayatype: window.$gz.type.WorkOrder }
|
|
});
|
|
}
|
|
break;
|
|
case "statuslist":
|
|
m.vm.$router.push({
|
|
name: "svc-work-order-status"
|
|
});
|
|
break;
|
|
case "itemstatuslist":
|
|
m.vm.$router.push({
|
|
name: "svc-work-order-item-status"
|
|
});
|
|
break;
|
|
case "prioritylist":
|
|
m.vm.$router.push({
|
|
name: "svc-work-order-item-priorities"
|
|
});
|
|
break;
|
|
default:
|
|
window.$gz.eventBus.$emit(
|
|
"notify-warning",
|
|
FORM_KEY + "::context click: [" + m.key + "]"
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////
|
|
//
|
|
//
|
|
function generateMenu(vm) {
|
|
let menuOptions = {
|
|
isMain: false,
|
|
readOnly: vm.formState.readOnly,
|
|
icon: "$ayiTools",
|
|
title: "WorkOrder",
|
|
helpUrl: "svc-workorder",
|
|
formData: {
|
|
ayaType: window.$gz.type.WorkOrder,
|
|
recordId: vm.$route.params.recordid,
|
|
formCustomTemplateKey: FORM_CUSTOM_TEMPLATE_KEY,
|
|
recordName: vm.obj.serial == 0 ? null : vm.obj.serial.toString()
|
|
},
|
|
menuItems: []
|
|
};
|
|
|
|
if (vm.rights.change) {
|
|
menuOptions.menuItems.push({
|
|
title: "Save",
|
|
icon: "$ayiSave",
|
|
surface: true,
|
|
key: FORM_KEY + ":save",
|
|
vm: vm
|
|
});
|
|
}
|
|
|
|
if (vm.rights.delete && vm.$route.params.recordid != 0) {
|
|
menuOptions.menuItems.push({
|
|
title: "Delete",
|
|
icon: "$ayiTrashAlt",
|
|
surface: false,
|
|
key: FORM_KEY + ":delete",
|
|
vm: vm
|
|
});
|
|
}
|
|
|
|
//REPORTS
|
|
//Report not Print, print is a further option
|
|
menuOptions.menuItems.push({
|
|
title: "Report",
|
|
icon: "$ayiFileAlt",
|
|
key: FORM_KEY + ":report",
|
|
vm: vm
|
|
});
|
|
|
|
//get last report selected
|
|
let lastReport = window.$gz.form.getLastReport(FORM_KEY);
|
|
if (lastReport != null) {
|
|
menuOptions.menuItems.push({
|
|
title: lastReport.name,
|
|
icon: "$ayiFileAlt",
|
|
key: FORM_KEY + ":report:" + lastReport.id,
|
|
vm: vm
|
|
});
|
|
}
|
|
|
|
if (vm.rights.change) {
|
|
menuOptions.menuItems.push({
|
|
title: "New",
|
|
icon: "$ayiPlus",
|
|
key: FORM_KEY + ":new",
|
|
vm: vm
|
|
});
|
|
}
|
|
|
|
if (vm.rights.change) {
|
|
menuOptions.menuItems.push({
|
|
title: "Duplicate",
|
|
icon: "$ayiClone",
|
|
key: FORM_KEY + ":duplicate",
|
|
vm: vm
|
|
});
|
|
}
|
|
|
|
//--- /show all ---
|
|
|
|
menuOptions.menuItems.push({ divider: true, inset: false });
|
|
menuOptions.menuItems.push({
|
|
title: "WorkOrderStatusList",
|
|
icon: "$ayiFlag",
|
|
key: FORM_KEY + ":statuslist",
|
|
vm: vm
|
|
});
|
|
menuOptions.menuItems.push({
|
|
title: "WorkOrderItemStatusList",
|
|
icon: "$ayiCircle",
|
|
key: FORM_KEY + ":itemstatuslist",
|
|
vm: vm
|
|
});
|
|
menuOptions.menuItems.push({
|
|
title: "WorkOrderItemPriorityList",
|
|
icon: "$ayiFireAlt",
|
|
key: FORM_KEY + ":prioritylist",
|
|
vm: vm
|
|
});
|
|
menuOptions.menuItems.push({ divider: true, inset: false });
|
|
|
|
window.$gz.eventBus.$emit("menu-change", menuOptions);
|
|
}
|
|
|
|
let JUST_DELETED = false;
|
|
|
|
/////////////////////////////////
|
|
//
|
|
//
|
|
async function initForm(vm) {
|
|
await fetchTranslatedText(vm);
|
|
await window.$gz.formCustomTemplate.get(FORM_CUSTOM_TEMPLATE_KEY, vm);
|
|
await populateSelectionLists(vm);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////
|
|
//
|
|
// Ensures UI translated text is available
|
|
//
|
|
async function fetchTranslatedText(vm) {
|
|
await window.$gz.translation.cacheTranslations([
|
|
"WorkOrder",
|
|
"Customer",
|
|
"WorkOrderSerialNumber",
|
|
"WorkOrderSummary",
|
|
"WorkOrderCloseByDate",
|
|
"Contract",
|
|
"Project",
|
|
"WorkOrderInvoiceNumber",
|
|
"WorkOrderServiceDate",
|
|
"WorkOrderCustomerContactName",
|
|
"WorkOrderCustomerReferenceNumber",
|
|
"WorkOrderInternalReferenceNumber",
|
|
"WorkOrderOnsite",
|
|
"NewStatus",
|
|
"WorkOrderStatus",
|
|
"WorkOrderCustom1",
|
|
"WorkOrderCustom2",
|
|
"WorkOrderCustom3",
|
|
"WorkOrderCustom4",
|
|
"WorkOrderCustom5",
|
|
"WorkOrderCustom6",
|
|
"WorkOrderCustom7",
|
|
"WorkOrderCustom8",
|
|
"WorkOrderCustom9",
|
|
"WorkOrderCustom10",
|
|
"WorkOrderCustom11",
|
|
"WorkOrderCustom12",
|
|
"WorkOrderCustom13",
|
|
"WorkOrderCustom14",
|
|
"WorkOrderCustom15",
|
|
"WorkOrderCustom16",
|
|
"WorkOrderItemList",
|
|
"WorkOrderItemTechNotes",
|
|
"WorkOrderItemScheduledUserList",
|
|
"WorkOrderItemSummary",
|
|
"WorkOrderItemWorkOrderStatusID",
|
|
"WorkOrderItemRequestDate",
|
|
"WorkOrderItemPriorityID",
|
|
"WorkOrderItemWarrantyService",
|
|
"WorkOrderItemScheduledUserStartDate",
|
|
"WorkOrderItemScheduledUserStopDate",
|
|
"WorkOrderItemScheduledUserEstimatedQuantity",
|
|
"WorkOrderItemScheduledUserUserID",
|
|
"WorkOrderItemScheduledUserServiceRateID",
|
|
"Sequence",
|
|
"WorkOrderItemUnitList",
|
|
"AddressTypePhysical",
|
|
"AddressTypePostal",
|
|
"AddressCopyToPostal",
|
|
"AddressCopyToPhysical",
|
|
"Address",
|
|
"AddressPostalDeliveryAddress",
|
|
"AddressPostalCity",
|
|
"AddressPostalStateProv",
|
|
"AddressPostalCountry",
|
|
"AddressPostalPostal",
|
|
"AddressDeliveryAddress",
|
|
"AddressCity",
|
|
"AddressStateProv",
|
|
"AddressCountry",
|
|
"AddressLatitude",
|
|
"AddressLongitude",
|
|
"SelectAlternateAddress",
|
|
"WorkOrderItemStatusList",
|
|
"WorkOrderStatusList",
|
|
"WorkOrderItemPriorityList",
|
|
"WorkOrderItemExpenseChargeAmount",
|
|
"WorkOrderItemExpenseChargeTaxCodeID",
|
|
"WorkOrderItemExpenseChargeToCustomer",
|
|
"WorkOrderItemExpenseDescription",
|
|
"WorkOrderItemExpenseList",
|
|
"WorkOrderItemExpenseName",
|
|
"WorkOrderItemExpenseReimburseUser",
|
|
"WorkOrderItemExpenseTaxPaid",
|
|
"WorkOrderItemExpenseTotalCost",
|
|
"WorkOrderItemExpenseUserID",
|
|
"WorkOrderItemLaborList",
|
|
"WorkOrderItemLaborServiceStartDate",
|
|
"WorkOrderItemLaborServiceStopDate",
|
|
"WorkOrderItemLaborServiceRateQuantity",
|
|
"WorkOrderItemLaborServiceRateID",
|
|
"WorkOrderItemLaborServiceDetails",
|
|
"WorkOrderItemLaborUserID",
|
|
"WorkOrderItemLaborNoChargeQuantity",
|
|
"WorkOrderItemLaborTaxRateSaleID",
|
|
"WorkOrderItemLaborPrice",
|
|
"WorkOrderItemTravelList",
|
|
"WorkOrderItemTravelStartDate",
|
|
"WorkOrderItemTravelStopDate",
|
|
"WorkOrderItemTravelRateQuantity",
|
|
"WorkOrderItemTravelServiceRateID",
|
|
"WorkOrderItemTravelDetails",
|
|
"WorkOrderItemTravelUserID",
|
|
"WorkOrderItemTravelNoChargeQuantity",
|
|
"WorkOrderItemTravelTaxRateSaleID",
|
|
"WorkOrderItemTravelPrice",
|
|
|
|
"SaveRecordToProceed",
|
|
"Cost",
|
|
"ListPrice",
|
|
"NetPrice",
|
|
"Price",
|
|
"PriceOverride",
|
|
"Tax",
|
|
"TaxAAmt",
|
|
"TaxBAmt",
|
|
"LineTotal",
|
|
"UnitOfMeasure"
|
|
]);
|
|
}
|
|
|
|
//////////////////////
|
|
//
|
|
//
|
|
async function populateSelectionLists(vm) {
|
|
let res = await window.$gz.api.get("work-order-status/list");
|
|
if (res.error) {
|
|
vm.formState.serverError = res.error;
|
|
window.$gz.form.setErrorBoxErrors(vm);
|
|
} else {
|
|
vm.selectLists.wostatus = res.data.all;
|
|
vm.selectLists.allowedwostatus = res.data.allowed;
|
|
}
|
|
|
|
res = await window.$gz.api.get("work-order-item-status/list");
|
|
if (res.error) {
|
|
vm.formState.serverError = res.error;
|
|
window.$gz.form.setErrorBoxErrors(vm);
|
|
} else {
|
|
vm.selectLists.woItemStatus = res.data;
|
|
}
|
|
|
|
res = await window.$gz.api.get("work-order-item-priority/list");
|
|
if (res.error) {
|
|
vm.formState.serverError = res.error;
|
|
window.$gz.form.setErrorBoxErrors(vm);
|
|
} else {
|
|
vm.selectLists.woItemPriorities = res.data;
|
|
}
|
|
|
|
await window.$gz.enums.fetchEnumList("WorkorderItemTaskCompletionType"); //prefetch
|
|
vm.selectLists.woItemTaskCompletionTypes = window.$gz.enums.getSelectionList(
|
|
"WorkorderItemTaskCompletionType"
|
|
);
|
|
|
|
//---------
|
|
/*
|
|
|
|
{
|
|
"data": {
|
|
"id": 10,
|
|
"concurrency": 7981529,
|
|
"serial": 10,
|
|
"notes": "Consequatur reprehenderit eveniet molestiae molestiae molestiae autem.",
|
|
"wiki": null,
|
|
"customFields": null,
|
|
"tags": [
|
|
"brown",
|
|
"red",
|
|
"zone8",
|
|
"zone9"
|
|
],
|
|
"customerId": 43,
|
|
"projectId": null,
|
|
"projectViz": null,
|
|
"internalReferenceNumber": "irf-6149",
|
|
"customerReferenceNumber": "crf-0272",
|
|
"customerContactName": "contact name here",
|
|
"fromQuoteId": null,
|
|
"fromPMId": null,
|
|
"fromCSRId": null,
|
|
"serviceDate": "2021-02-01T17:00:00Z",
|
|
"completeByDate": "2021-02-06T17:00:00Z",
|
|
"durationToCompleted": "00:00:00",
|
|
"invoiceNumber": null,
|
|
"customerSignature": null,
|
|
"customerSignatureName": null,
|
|
"customerSignatureCaptured": null,
|
|
"techSignature": null,
|
|
"techSignatureName": null,
|
|
"techSignatureCaptured": null,
|
|
"onsite": false,
|
|
"contractId": null,
|
|
"contractViz": "-",
|
|
"postAddress": "6304 Eduardo Lights",
|
|
"postCity": "Schoenfort",
|
|
"postRegion": "South Dakota",
|
|
"postCountry": "Liberia",
|
|
"postCode": "03315",
|
|
"address": "750 Darrell Cliff",
|
|
"city": "Armstrongview",
|
|
"region": "Illinois",
|
|
"country": "Israel",
|
|
"latitude": 9.8268,
|
|
"longitude": 34.3788,
|
|
"items": [
|
|
{
|
|
"id": 20,
|
|
"concurrency": 7981529,
|
|
"notes": "itemnotes - 0 ",
|
|
"wiki": null,
|
|
"customFields": null,
|
|
"tags": [],
|
|
"workOrderId": 10,
|
|
"techNotes": "technotes - 0",
|
|
"workorderItemStatusId": 1,
|
|
"workorderItemPriorityId": 5,
|
|
"requestDate": "2021-02-01T17:00:00Z",
|
|
"warrantyService": false,
|
|
"sequence": 1,
|
|
"isDirty": false,
|
|
"expenses": [
|
|
{
|
|
"id": 39,
|
|
"concurrency": 7981529,
|
|
"description": null,
|
|
"name": "Small Soft Pants",
|
|
"totalCost": 5.459658245770105,
|
|
"chargeAmount": 6.551589894924126,
|
|
"taxPaid": 0,
|
|
"chargeTaxCodeId": 2,
|
|
"chargeTaxCodeViz": "Goods only",
|
|
"reimburseUser": true,
|
|
"userId": 43,
|
|
"userViz": "Carolyne Watsica",
|
|
"chargeToCustomer": true,
|
|
"isDirty": false,
|
|
"workOrderItemId": 20
|
|
},
|
|
{
|
|
"id": 40,
|
|
"concurrency": 7981529,
|
|
"description": null,
|
|
"name": "Tasty Wooden Salad",
|
|
"totalCost": 10.91931649154021,
|
|
"chargeAmount": 12.011248140694232,
|
|
"taxPaid": 0,
|
|
"chargeTaxCodeId": 2,
|
|
"chargeTaxCodeViz": "Goods only",
|
|
"reimburseUser": true,
|
|
"userId": 25,
|
|
"userViz": "Dereck Romaguera",
|
|
"chargeToCustomer": true,
|
|
"isDirty": false,
|
|
"workOrderItemId": 20
|
|
}
|
|
],
|
|
"labors": [
|
|
{
|
|
"id": 39,
|
|
"concurrency": 7981529,
|
|
"userId": 25,
|
|
"userViz": "Dereck Romaguera",
|
|
"serviceStartDate": "2021-02-01T17:00:00Z",
|
|
"serviceStopDate": "2021-02-01T18:00:00Z",
|
|
"serviceRateId": 6,
|
|
"serviceRateViz": "Purple TV",
|
|
"serviceDetails": "Error autem quaerat voluptatem.",
|
|
"serviceRateQuantity": 1,
|
|
"noChargeQuantity": 0,
|
|
"serviceBankId": null,
|
|
"taxCodeSaleId": null,
|
|
"taxCodeSaleViz": null,
|
|
"cost": 0,
|
|
"listPrice": 0,
|
|
"price": 0,
|
|
"taxName": null,
|
|
"taxAPct": 0,
|
|
"taxBPct": 0,
|
|
"taxOnTax": false,
|
|
"taxAViz": 0,
|
|
"taxBViz": 0,
|
|
"lineTotalViz": 0,
|
|
"isDirty": false,
|
|
"workOrderItemId": 20
|
|
},
|
|
{
|
|
"id": 40,
|
|
"concurrency": 7981529,
|
|
"userId": 22,
|
|
"userViz": "Georgette Dooley - CustomerLimited",
|
|
"serviceStartDate": "2021-02-01T17:00:00Z",
|
|
"serviceStopDate": "2021-02-01T18:00:00Z",
|
|
"serviceRateId": 2,
|
|
"serviceRateViz": "Silver service rate",
|
|
"serviceDetails": "Ipsam id officiis non.",
|
|
"serviceRateQuantity": 2,
|
|
"noChargeQuantity": 0,
|
|
"serviceBankId": null,
|
|
"taxCodeSaleId": null,
|
|
"taxCodeSaleViz": null,
|
|
"cost": 0,
|
|
"listPrice": 0,
|
|
"price": 0,
|
|
"taxName": null,
|
|
"taxAPct": 0,
|
|
"taxBPct": 0,
|
|
"taxOnTax": false,
|
|
"taxAViz": 0,
|
|
"taxBViz": 0,
|
|
"lineTotalViz": 0,
|
|
"isDirty": false,
|
|
"workOrderItemId": 20
|
|
}
|
|
],
|
|
"loans": [],
|
|
"parts": [],
|
|
"partRequests": [],
|
|
"scheduledUsers": [
|
|
{
|
|
"id": 39,
|
|
"concurrency": 7981529,
|
|
"userId": 40,
|
|
"userViz": "Raul Berge",
|
|
"estimatedQuantity": 1,
|
|
"startDate": "2021-02-01T17:00:00Z",
|
|
"stopDate": "2021-02-01T18:00:00Z",
|
|
"serviceRateId": null,
|
|
"serviceRateViz": null,
|
|
"isDirty": false,
|
|
"workOrderItemId": 20
|
|
},
|
|
{
|
|
"id": 40,
|
|
"concurrency": 7981529,
|
|
"userId": 37,
|
|
"userViz": "Caleb Kovacek",
|
|
"estimatedQuantity": 2,
|
|
"startDate": "2021-02-01T17:00:00Z",
|
|
"stopDate": "2021-02-01T18:00:00Z",
|
|
"serviceRateId": null,
|
|
"serviceRateViz": null,
|
|
"isDirty": false,
|
|
"workOrderItemId": 20
|
|
}
|
|
],
|
|
"tasks": [],
|
|
"travels": [],
|
|
"units": [],
|
|
"outsideServices": []
|
|
},
|
|
{
|
|
"id": 21,
|
|
"concurrency": 7981529,
|
|
"notes": "itemnotes - 1 ",
|
|
"wiki": null,
|
|
"customFields": null,
|
|
"tags": [],
|
|
"workOrderId": 10,
|
|
"techNotes": "technotes - 1",
|
|
"workorderItemStatusId": 1,
|
|
"workorderItemPriorityId": 1,
|
|
"requestDate": "2021-02-01T17:01:00Z",
|
|
"warrantyService": false,
|
|
"sequence": 2,
|
|
"isDirty": false,
|
|
"expenses": [
|
|
{
|
|
"id": 41,
|
|
"concurrency": 7981529,
|
|
"description": null,
|
|
"name": "Licensed Concrete Sausages",
|
|
"totalCost": 4.596012142764409,
|
|
"chargeAmount": 5.515214571317291,
|
|
"taxPaid": 0,
|
|
"chargeTaxCodeId": 2,
|
|
"chargeTaxCodeViz": "Goods only",
|
|
"reimburseUser": true,
|
|
"userId": 37,
|
|
"userViz": "Caleb Kovacek",
|
|
"chargeToCustomer": true,
|
|
"isDirty": false,
|
|
"workOrderItemId": 21
|
|
},
|
|
{
|
|
"id": 42,
|
|
"concurrency": 7981529,
|
|
"description": null,
|
|
"name": "Fantastic Concrete Fish",
|
|
"totalCost": 9.192024285528818,
|
|
"chargeAmount": 10.111226714081699,
|
|
"taxPaid": 0,
|
|
"chargeTaxCodeId": 2,
|
|
"chargeTaxCodeViz": "Goods only",
|
|
"reimburseUser": true,
|
|
"userId": 20,
|
|
"userViz": "Reta Braun - HeadOffice",
|
|
"chargeToCustomer": true,
|
|
"isDirty": false,
|
|
"workOrderItemId": 21
|
|
}
|
|
],
|
|
"labors": [
|
|
{
|
|
"id": 41,
|
|
"concurrency": 7981529,
|
|
"userId": 34,
|
|
"userViz": "Matilda Mraz",
|
|
"serviceStartDate": "2021-02-01T17:00:00Z",
|
|
"serviceStopDate": "2021-02-01T18:00:00Z",
|
|
"serviceRateId": 4,
|
|
"serviceRateViz": "Salmon PA",
|
|
"serviceDetails": "Voluptatibus eos facilis impedit iusto atque nulla.",
|
|
"serviceRateQuantity": 1,
|
|
"noChargeQuantity": 0,
|
|
"serviceBankId": null,
|
|
"taxCodeSaleId": null,
|
|
"taxCodeSaleViz": null,
|
|
"cost": 0,
|
|
"listPrice": 0,
|
|
"price": 0,
|
|
"taxName": null,
|
|
"taxAPct": 0,
|
|
"taxBPct": 0,
|
|
"taxOnTax": false,
|
|
"taxAViz": 0,
|
|
"taxBViz": 0,
|
|
"lineTotalViz": 0,
|
|
"isDirty": false,
|
|
"workOrderItemId": 21
|
|
},
|
|
{
|
|
"id": 42,
|
|
"concurrency": 7981529,
|
|
"userId": 36,
|
|
"userViz": "Chasity VonRueden",
|
|
"serviceStartDate": "2021-02-01T17:00:00Z",
|
|
"serviceStopDate": "2021-02-01T18:00:00Z",
|
|
"serviceRateId": 2,
|
|
"serviceRateViz": "Silver service rate",
|
|
"serviceDetails": "Quibusdam autem ut aut.",
|
|
"serviceRateQuantity": 2,
|
|
"noChargeQuantity": 0,
|
|
"serviceBankId": null,
|
|
"taxCodeSaleId": null,
|
|
"taxCodeSaleViz": null,
|
|
"cost": 0,
|
|
"listPrice": 0,
|
|
"price": 0,
|
|
"taxName": null,
|
|
"taxAPct": 0,
|
|
"taxBPct": 0,
|
|
"taxOnTax": false,
|
|
"taxAViz": 0,
|
|
"taxBViz": 0,
|
|
"lineTotalViz": 0,
|
|
"isDirty": false,
|
|
"workOrderItemId": 21
|
|
}
|
|
],
|
|
"loans": [],
|
|
"parts": [],
|
|
"partRequests": [],
|
|
"scheduledUsers": [
|
|
{
|
|
"id": 41,
|
|
"concurrency": 7981529,
|
|
"userId": 27,
|
|
"userViz": "Oran Kreiger",
|
|
"estimatedQuantity": 1,
|
|
"startDate": "2021-02-01T17:00:00Z",
|
|
"stopDate": "2021-02-01T18:00:00Z",
|
|
"serviceRateId": null,
|
|
"serviceRateViz": null,
|
|
"isDirty": false,
|
|
"workOrderItemId": 21
|
|
},
|
|
{
|
|
"id": 42,
|
|
"concurrency": 7981529,
|
|
"userId": 29,
|
|
"userViz": "Luisa Mayer",
|
|
"estimatedQuantity": 2,
|
|
"startDate": "2021-02-01T17:00:00Z",
|
|
"stopDate": "2021-02-01T18:00:00Z",
|
|
"serviceRateId": null,
|
|
"serviceRateViz": null,
|
|
"isDirty": false,
|
|
"workOrderItemId": 21
|
|
}
|
|
],
|
|
"tasks": [],
|
|
"travels": [],
|
|
"units": [],
|
|
"outsideServices": []
|
|
}
|
|
],
|
|
"states": [
|
|
{
|
|
"id": 37,
|
|
"concurrency": 7981529,
|
|
"workOrderId": 10,
|
|
"workOrderStatusId": 2,
|
|
"created": "2021-02-01T17:05:00Z",
|
|
"userId": 34,
|
|
"userViz": "Matilda Mraz"
|
|
},
|
|
{
|
|
"id": 38,
|
|
"concurrency": 7981529,
|
|
"workOrderId": 10,
|
|
"workOrderStatusId": 3,
|
|
"created": "2021-02-01T18:00:00Z",
|
|
"userId": 37,
|
|
"userViz": "Caleb Kovacek"
|
|
},
|
|
{
|
|
"id": 39,
|
|
"concurrency": 7981529,
|
|
"workOrderId": 10,
|
|
"workOrderStatusId": 1,
|
|
"created": "2021-02-01T18:05:00Z",
|
|
"userId": 21,
|
|
"userViz": "Johnnie Mueller - CustomerFull"
|
|
},
|
|
{
|
|
"id": 40,
|
|
"concurrency": 7981529,
|
|
"workOrderId": 10,
|
|
"workOrderStatusId": 3,
|
|
"created": "2021-02-01T19:00:00Z",
|
|
"userId": 41,
|
|
"userViz": "Palma Huel"
|
|
}
|
|
],
|
|
"isDirty": false,
|
|
"isLockedAtServer": false,
|
|
"alertViz": null
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
</script>
|