2473 lines
71 KiB
Vue
2473 lines
71 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" pop-alert></gz-alert>
|
|
<gz-alert :alert-message="alertTechNotes"></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"
|
|
:goto="this.goto"
|
|
data-cy="woItems"
|
|
class="mt-16"
|
|
@change="setDirty()"
|
|
/>
|
|
</v-form>
|
|
</div>
|
|
<v-overlay
|
|
:value="!formState.ready || formState.loading"
|
|
data-cy="busyoverlay"
|
|
>
|
|
<v-progress-circular indeterminate :size="64" />
|
|
</v-overlay>
|
|
<!-- ################################################################################-->
|
|
<!-- ########################## DUPLICATE DIALOG ###############################-->
|
|
<!-- ################################################################################-->
|
|
<template>
|
|
<v-row dense justify="center">
|
|
<v-dialog v-model="duplicateDlg" persistent max-width="600px">
|
|
<v-card>
|
|
<v-card-title>{{ duplicateDlgTitle }}</v-card-title>
|
|
<v-card-text>
|
|
<v-checkbox
|
|
v-model="genCopyWiki"
|
|
dense
|
|
:label="$ay.t('CopyWiki')"
|
|
></v-checkbox>
|
|
|
|
<v-checkbox
|
|
v-model="genCopyAttachments"
|
|
dense
|
|
:label="$ay.t('CopyAttachments')"
|
|
></v-checkbox>
|
|
</v-card-text>
|
|
<v-card-actions>
|
|
<v-btn text color="primary" @click="duplicateDlg = false">{{
|
|
$ay.t("Cancel")
|
|
}}</v-btn>
|
|
<v-spacer></v-spacer>
|
|
|
|
<v-btn color="primary" text @click="duplicateHandler()">{{
|
|
$ay.t("OK")
|
|
}}</v-btn>
|
|
</v-card-actions>
|
|
</v-card>
|
|
</v-dialog>
|
|
</v-row>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
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";
|
|
|
|
export default {
|
|
components: {
|
|
GzWoHeader,
|
|
GzWoItems
|
|
},
|
|
data() {
|
|
return {
|
|
formCustomTemplateKey: FORM_CUSTOM_TEMPLATE_KEY,
|
|
obj: {
|
|
id: 0,
|
|
concurrency: 0,
|
|
serial: 0,
|
|
notes: null,
|
|
wiki: null,
|
|
customFields: "{}",
|
|
tags: [],
|
|
customerId: 0, //part of rule breaking, flip to null on new triggers broken rule
|
|
projectId: null,
|
|
contractId: null,
|
|
internalReferenceNumber: null,
|
|
customerReferenceNumber: null,
|
|
customerContactName: null,
|
|
fromQuoteId: null,
|
|
fromPMId: null,
|
|
serviceDate: window.$gz.locale.nowUTC8601String(),
|
|
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(),
|
|
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(),
|
|
selectLists: {
|
|
wostatus: [],
|
|
allowedwostatus: [],
|
|
woItemPriorities: [],
|
|
woItemStatus: [],
|
|
woItemTaskCompletionTypes: [],
|
|
loanUnitRateUnits: []
|
|
},
|
|
maxTableNotesLength: 50, //value to cut off notes in tables
|
|
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,
|
|
goto: null, //{type:ayatype,id:wodescendant id} picked up by woitem when set non-null to trigger navigate to the item in question
|
|
duplicateDlg: false,
|
|
duplicateDlgTitle: null,
|
|
duplicateTo: null, //"pm","wo" or "quote"
|
|
genCopyWiki: false,
|
|
genCopyAttachments: false
|
|
};
|
|
},
|
|
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: "#ffffffff", //invisible
|
|
completed: false,
|
|
locked: false
|
|
};
|
|
},
|
|
useInventory() {
|
|
return window.$gz.store.state.globalSettings.useInventory;
|
|
},
|
|
alertTechNotes() {
|
|
return this.obj.customerTechNotesViz
|
|
? `${this.$ay.t("CustomerTechNotes")}\n${this.obj.customerTechNotesViz}`
|
|
: null;
|
|
}
|
|
},
|
|
watch: {
|
|
formState: {
|
|
handler: function(val) {
|
|
if (this.formState.loading) {
|
|
return;
|
|
}
|
|
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");
|
|
}
|
|
|
|
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
|
|
}
|
|
},
|
|
async created() {
|
|
const vm = this;
|
|
|
|
try {
|
|
await initForm(vm);
|
|
vm.rights = window.$gz.role.getRights(window.$gz.type.WorkOrder);
|
|
window.$gz.eventBus.$on("menu-click", clickHandler);
|
|
let setDirty = false;
|
|
//id 0 means create or duplicate to new
|
|
if (vm.$route.params.recordid != 0) {
|
|
//open existing path
|
|
//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);
|
|
//check for copy wo item on route params
|
|
const wi = this.$route.params.copyItem;
|
|
if (wi) {
|
|
this.washWorkOrderItem(wi);
|
|
wi.workOrderId = vm.obj.id;
|
|
wi.sequence = vm.obj.items.length + 1;
|
|
vm.obj.items.push(wi);
|
|
setDirty = true;
|
|
}
|
|
}
|
|
} else {
|
|
//new path
|
|
if (this.$route.params.obj) {
|
|
//DUPLICATE OR GEN FROM CSR, QUOTE OR PM
|
|
//A whole work order basically is presented here
|
|
this.obj = this.$route.params.obj;
|
|
|
|
this.obj.concurrency = undefined;
|
|
this.obj.id = 0;
|
|
this.obj.serial = 0;
|
|
this.obj.isDirty = true;
|
|
//set the service date if not set by the caller
|
|
if (this.obj.serviceDate == null) {
|
|
this.obj.serviceDate = window.$gz.locale.nowUTC8601String();
|
|
}
|
|
vm.obj.items.forEach(z => {
|
|
z.workOrderId = 0;
|
|
this.washWorkOrderItem(z);
|
|
});
|
|
setDirty = true;
|
|
} else {
|
|
//sometimes we're here with some data to fill in (e.g. schedule creates new wo itemscheduled user, if so there will be a add property on the params)
|
|
|
|
if (this.$route.params.add) {
|
|
const n = this.$route.params.add;
|
|
switch (n.type) {
|
|
case this.$ay.ayt().WorkOrderItemScheduledUser:
|
|
this.obj.items.push({
|
|
id: 0,
|
|
concurrency: 0,
|
|
notes: "-", //todo: default here at some point or just leave this TTM
|
|
wiki: null,
|
|
customFields: "{}",
|
|
tags: [],
|
|
workOrderId: null,
|
|
techNotes: null,
|
|
workOrderItemStatusId: null,
|
|
workOrderItemPriorityId: null,
|
|
requestDate: null,
|
|
warrantyService: false,
|
|
sequence: 1, //indexes are zero based but sequences are visible to user so 1 based
|
|
isDirty: true,
|
|
expenses: [],
|
|
labors: [],
|
|
loans: [],
|
|
parts: [],
|
|
partRequests: [],
|
|
scheduledUsers: [
|
|
{
|
|
id: 0,
|
|
concurrency: 0,
|
|
userId: n.userId,
|
|
estimatedQuantity: window.$gz.locale.diffHoursFromUTC8601String(
|
|
n.start,
|
|
n.end
|
|
),
|
|
startDate: n.start,
|
|
stopDate: n.end,
|
|
serviceRateId: null,
|
|
isDirty: true,
|
|
workOrderItemId: 0,
|
|
uid: Date.now(),
|
|
userViz: n.name,
|
|
serviceRateViz: null
|
|
}
|
|
],
|
|
tasks: [],
|
|
travels: [],
|
|
units: [],
|
|
outsideServices: [],
|
|
uid: Date.now() //used for error tracking / display
|
|
});
|
|
|
|
this.obj.serviceDate = n.start;
|
|
break;
|
|
default:
|
|
if (window.$gz.dev) {
|
|
throw new Error(
|
|
`svc-workorder:created - new from Add - type '${n.type}' not recognized`
|
|
);
|
|
}
|
|
console.error(
|
|
`svc-workorder:created - new from Add - type '${n.type}' not recognized`
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
//NEW
|
|
this.formState.loading = false;
|
|
this.formState.ready = true;
|
|
//trigger rule breaking / validation
|
|
this.$nextTick(() => {
|
|
this.obj.customerId = null;
|
|
this.fieldValueChanged(`customerId`);
|
|
});
|
|
}
|
|
}
|
|
|
|
window.$gz.form.setFormState({
|
|
vm: vm,
|
|
loading: false,
|
|
dirty: setDirty,
|
|
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;
|
|
//navigate somewhere??
|
|
if (this.$route.params.gotype != null) {
|
|
//find out which woitem and then find out which type to set and then set them as the active item somehow
|
|
const gotype = Number(this.$route.params.gotype);
|
|
const goid = Number(this.$route.params.goid);
|
|
if (gotype != window.$gz.type.WorkOrder) {
|
|
this.$nextTick(() => {
|
|
//not workorder? Then must be a descendant so let's gooooooooooo!
|
|
this.goto = { type: gotype, id: goid };
|
|
});
|
|
}
|
|
}
|
|
}
|
|
},
|
|
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);
|
|
},
|
|
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) {
|
|
if (res.error.code == "2010") {
|
|
window.$gz.form.handleObjectNotFound(vm);
|
|
}
|
|
vm.formState.serverError = res.error;
|
|
window.$gz.form.setErrorBoxErrors(vm);
|
|
} else {
|
|
//assign opening values
|
|
res.data.isDirty = false;
|
|
res.data.items.forEach((z, index) => {
|
|
z.uid = index;
|
|
z.isDirty = false;
|
|
z.expenses.forEach((x, index) => {
|
|
x.uid = index;
|
|
x.isDirty = false;
|
|
});
|
|
z.labors.forEach((x, index) => {
|
|
x.uid = index;
|
|
x.isDirty = false;
|
|
});
|
|
z.loans.forEach((x, index) => {
|
|
x.uid = index;
|
|
x.isDirty = false;
|
|
});
|
|
z.parts.forEach((x, index) => {
|
|
x.uid = index;
|
|
x.isDirty = false;
|
|
});
|
|
z.partRequests.forEach((x, index) => {
|
|
x.uid = index;
|
|
x.isDirty = false;
|
|
});
|
|
z.scheduledUsers.forEach((x, index) => {
|
|
x.uid = index;
|
|
x.isDirty = false;
|
|
});
|
|
z.tasks.forEach((x, index) => {
|
|
x.uid = index;
|
|
x.isDirty = false;
|
|
});
|
|
z.travels.forEach((x, index) => {
|
|
x.uid = index;
|
|
x.isDirty = false;
|
|
});
|
|
z.units.forEach((x, index) => {
|
|
x.uid = index;
|
|
x.isDirty = false;
|
|
x.warrantyViz = null; //locally added field for reactivity, set on demand in UI not by server
|
|
});
|
|
z.outsideServices.forEach((x, index) => {
|
|
x.uid = index;
|
|
x.isDirty = false;
|
|
});
|
|
});
|
|
|
|
//insert into vue data
|
|
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
|
|
|
|
generateMenu(vm);
|
|
//update which areas are available to user
|
|
updateRights(vm);
|
|
|
|
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 submit() {
|
|
const vm = this;
|
|
if (vm.canSave == false) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
window.$gz.form.setFormState({
|
|
vm: vm,
|
|
loading: true
|
|
});
|
|
|
|
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.replace({
|
|
name: "workorder-edit",
|
|
params: {
|
|
recordid: vm.obj.id
|
|
// 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
|
|
// });
|
|
|
|
this.$nextTick(() => {
|
|
window.$gz.form.setFormState({
|
|
vm: vm,
|
|
loading: false
|
|
});
|
|
});
|
|
}
|
|
},
|
|
async remove() {
|
|
try {
|
|
const dialogResult = await window.$gz.dialog.confirmDelete();
|
|
if (dialogResult != true) {
|
|
return;
|
|
}
|
|
|
|
window.$gz.form.setFormState({
|
|
vm: this,
|
|
loading: true
|
|
});
|
|
|
|
if (this.$route.params.recordid == 0) {
|
|
JUST_DELETED = true;
|
|
|
|
this.$router.go(-1);
|
|
} else {
|
|
window.$gz.form.deleteAllErrorBoxErrors(this);
|
|
const res = await window.$gz.api.remove(
|
|
API_BASE_URL + this.$route.params.recordid
|
|
);
|
|
if (res.error) {
|
|
this.formState.serverError = res.error;
|
|
window.$gz.form.setErrorBoxErrors(this);
|
|
} else {
|
|
JUST_DELETED = true;
|
|
this.$router.go(-1);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
window.$gz.errorHandler.handleFormError(error, this);
|
|
} finally {
|
|
window.$gz.form.setFormState({
|
|
vm: this,
|
|
loading: false
|
|
});
|
|
}
|
|
},
|
|
duplicateHandler() {
|
|
switch (this.duplicateTo) {
|
|
case "pm":
|
|
this.generatePM();
|
|
break;
|
|
case "wo":
|
|
this.duplicate();
|
|
break;
|
|
case "quote":
|
|
this.generateQuote();
|
|
break;
|
|
}
|
|
},
|
|
duplicate() {
|
|
const cp = JSON.parse(JSON.stringify(this.obj));
|
|
if (this.genCopyAttachments) {
|
|
//this property set will trigger server to copy attachments
|
|
cp.genCopyAttachmentsFrom = {
|
|
sAType: window.$gz.type.WorkOrder,
|
|
sId: this.obj.id
|
|
};
|
|
}
|
|
if (!this.genCopyWiki) {
|
|
cp.wiki = null; //already copied, need to remove it instead
|
|
}
|
|
|
|
this.$router.push({
|
|
name: "workorder-edit",
|
|
params: {
|
|
recordid: 0,
|
|
obj: cp
|
|
}
|
|
});
|
|
},
|
|
generateQuote() {
|
|
const cp = JSON.parse(JSON.stringify(this.obj));
|
|
if (this.genCopyAttachments) {
|
|
//this property set will trigger server to copy attachments
|
|
cp.genCopyAttachmentsFrom = {
|
|
sAType: window.$gz.type.WorkOrder,
|
|
sId: this.obj.id
|
|
};
|
|
}
|
|
if (!this.genCopyWiki) {
|
|
cp.wiki = null; //already copied, need to remove it instead
|
|
}
|
|
cp.states = [];
|
|
cp.lastStatusId = undefined;
|
|
cp.items.forEach(x => {
|
|
x.partRequests = [];
|
|
});
|
|
cp.id = 0;
|
|
|
|
//set quote specific fields
|
|
cp.preparedById = window.$gz.store.state.userId;
|
|
cp.introduction = null;
|
|
cp.requested = window.$gz.locale.nowUTC8601String();
|
|
cp.validUntil = null;
|
|
cp.submitted = null;
|
|
cp.approved = null;
|
|
|
|
this.$router.push({
|
|
name: "quote-edit",
|
|
params: {
|
|
recordid: 0,
|
|
obj: cp
|
|
}
|
|
});
|
|
},
|
|
generatePM() {
|
|
const cp = JSON.parse(JSON.stringify(this.obj));
|
|
if (this.genCopyAttachments) {
|
|
//this property set will trigger server to copy attachments
|
|
cp.genCopyAttachmentsFrom = {
|
|
sAType: window.$gz.type.WorkOrder,
|
|
sId: this.obj.id
|
|
};
|
|
}
|
|
if (!this.genCopyWiki) {
|
|
cp.wiki = null; //already copied, need to remove it instead
|
|
}
|
|
cp.states = [];
|
|
cp.items.forEach(x => {
|
|
x.partRequests = [];
|
|
});
|
|
cp.id = 0;
|
|
//PM Defaults copied from PM data object
|
|
cp.stopGeneratingDate = null;
|
|
cp.excludeDaysOfWeek = 96;
|
|
cp.active = true;
|
|
cp.nextServiceDate = this.obj.serviceDate;
|
|
cp.repeatUnit = 7;
|
|
cp.generateBeforeUnit = 4;
|
|
cp.repeatInterval = 1;
|
|
cp.generateBeforeInterval = 14;
|
|
cp.generateDate = null;
|
|
|
|
this.$router.push({
|
|
name: "pm-edit",
|
|
params: {
|
|
recordid: 0,
|
|
obj: cp
|
|
}
|
|
});
|
|
},
|
|
/////////////////////////////////////////////////////////
|
|
// Clean woitem and children so it's
|
|
// savable as a new record
|
|
// (used by duplicate and copy wo item functions
|
|
// also called from work-order-items.vue copy woitem when
|
|
// self target)
|
|
//
|
|
washWorkOrderItem(wi) {
|
|
if (wi) {
|
|
wi.id = 0;
|
|
wi.concurrency = 0;
|
|
wi.uid = Date.now();
|
|
wi.isDirty = true;
|
|
if (!wi.keepCSR) {
|
|
//workaround so when sent from CSR it doesn't erase it
|
|
wi.fromCSRId = null;
|
|
}
|
|
if (wi.partRequests) {
|
|
wi.partRequests.splice(0);
|
|
}
|
|
wi.expenses.forEach(x => {
|
|
x.id = 0;
|
|
x.workOrderItemId = 0;
|
|
x.concurrency = undefined;
|
|
x.isDirty = true;
|
|
});
|
|
wi.labors.forEach(x => {
|
|
x.id = 0;
|
|
x.workOrderItemId = 0;
|
|
x.concurrency = undefined;
|
|
x.isDirty = true;
|
|
});
|
|
wi.loans.forEach(x => {
|
|
x.id = 0;
|
|
x.workOrderItemId = 0;
|
|
x.concurrency = undefined;
|
|
x.isDirty = true;
|
|
x.cost = 0;
|
|
x.listPrice = 0;
|
|
});
|
|
wi.parts.forEach(x => {
|
|
x.id = 0;
|
|
x.workOrderItemId = 0;
|
|
x.concurrency = undefined;
|
|
x.isDirty = true;
|
|
x.cost = 0;
|
|
x.listPrice = 0;
|
|
});
|
|
|
|
wi.scheduledUsers.forEach(x => {
|
|
x.id = 0;
|
|
x.workOrderItemId = 0;
|
|
x.concurrency = undefined;
|
|
x.isDirty = true;
|
|
});
|
|
wi.tasks.forEach(x => {
|
|
x.id = 0;
|
|
x.workOrderItemId = 0;
|
|
x.concurrency = undefined;
|
|
x.isDirty = true;
|
|
});
|
|
wi.travels.forEach(x => {
|
|
x.id = 0;
|
|
x.workOrderItemId = 0;
|
|
x.concurrency = undefined;
|
|
x.isDirty = true;
|
|
});
|
|
wi.units.forEach(x => {
|
|
x.id = 0;
|
|
x.workOrderItemId = 0;
|
|
x.concurrency = undefined;
|
|
x.isDirty = true;
|
|
});
|
|
wi.outsideServices.forEach(x => {
|
|
x.id = 0;
|
|
x.workOrderItemId = 0;
|
|
x.concurrency = undefined;
|
|
x.isDirty = true;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
//########################################## 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
|
|
/* eslint-disable no-unused-vars */
|
|
const { items: removedKey1, states: removedKey2, ...headerOnly } = vm.obj;
|
|
/* eslint-enable no-unused-vars */
|
|
//remove *Viz keys so they don't generate traffic
|
|
headerOnly.alertViz = undefined;
|
|
headerOnly.projectViz = undefined;
|
|
headerOnly.contractViz = undefined;
|
|
headerOnly.customerViz = undefined;
|
|
|
|
const 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?
|
|
const totalItems = vm.obj.states.length;
|
|
if (totalItems == 0) {
|
|
return;
|
|
}
|
|
for (let i = 0; i < totalItems; i++) {
|
|
const o = vm.obj.states[i];
|
|
if (o.concurrency == null) {
|
|
//it's new so save it
|
|
const 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;
|
|
|
|
//changing status updates the laststatus in the wo header so need to update concurrency here as well
|
|
vm.obj.concurrency = res.data.newWOConcurrency;
|
|
|
|
//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;
|
|
}
|
|
const 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++) {
|
|
if (vm.obj.items[i].isDirty) {
|
|
//get copy of item without child collections for independant submit
|
|
/* eslint-disable no-unused-vars */
|
|
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];
|
|
/* eslint-enable no-unused-vars */
|
|
const isPost = o.id == 0;
|
|
const 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 item id so they can save
|
|
vm.obj.items[i].units.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].tasks.forEach(
|
|
z => (z.workOrderItemId = vm.obj.items[i].id)
|
|
);
|
|
|
|
vm.obj.items[i].parts.forEach(
|
|
z => (z.workOrderItemId = vm.obj.items[i].id)
|
|
);
|
|
|
|
vm.obj.items[i].partRequests.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)
|
|
);
|
|
|
|
vm.obj.items[i].expenses.forEach(
|
|
z => (z.workOrderItemId = vm.obj.items[i].id)
|
|
);
|
|
|
|
vm.obj.items[i].loans.forEach(
|
|
z => (z.workOrderItemId = vm.obj.items[i].id)
|
|
);
|
|
vm.obj.items[i].outsideServices.forEach(
|
|
z => (z.workOrderItemId = vm.obj.items[i].id)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
//------
|
|
//save grandchildren
|
|
if (!vm.saveResult.fatal) {
|
|
await saveUnits(vm, i);
|
|
}
|
|
if (!vm.saveResult.fatal) {
|
|
await saveScheduledUsers(vm, i);
|
|
}
|
|
if (!vm.saveResult.fatal) {
|
|
await saveTasks(vm, i);
|
|
}
|
|
if (!vm.saveResult.fatal) {
|
|
await saveParts(vm, i);
|
|
}
|
|
if (!vm.saveResult.fatal) {
|
|
await savePartRequests(vm, i);
|
|
}
|
|
if (!vm.saveResult.fatal) {
|
|
await saveLabors(vm, i);
|
|
}
|
|
if (!vm.saveResult.fatal) {
|
|
await saveTravels(vm, i);
|
|
}
|
|
if (!vm.saveResult.fatal) {
|
|
await saveExpenses(vm, i);
|
|
}
|
|
if (!vm.saveResult.fatal) {
|
|
await saveLoans(vm, i);
|
|
}
|
|
if (!vm.saveResult.fatal) {
|
|
await saveOutsideServices(vm, i);
|
|
}
|
|
}
|
|
}
|
|
//####################################################################################
|
|
//## GRANDCHILDREN
|
|
|
|
/////////////////////////////
|
|
// UNITS
|
|
//
|
|
async function deleteUnits(vm, woItemIndex) {
|
|
//walk the array backwards as items may be spliced out
|
|
for (var i = vm.obj.items[woItemIndex].units.length - 1; i >= 0; i--) {
|
|
const d = vm.obj.items[woItemIndex].units[i];
|
|
if (!d.deleted) {
|
|
continue;
|
|
}
|
|
if (d.id != 0) {
|
|
const res = await window.$gz.api.remove(
|
|
`${API_BASE_URL}items/units/${d.id}`
|
|
);
|
|
if (res.error) {
|
|
handleSaveError(vm, {
|
|
error: res.error,
|
|
itemUid: vm.obj.items[woItemIndex].uid,
|
|
childKey: "units",
|
|
childUid: d.uid
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
vm.obj.items[woItemIndex].units.splice(i, 1);
|
|
}
|
|
|
|
//----
|
|
return;
|
|
}
|
|
|
|
async function saveUnits(vm, woItemIndex) {
|
|
//DELETE FLAGGED ITEMS FIRST
|
|
await deleteUnits(vm, woItemIndex);
|
|
if (vm.saveResult.fatal) {
|
|
return;
|
|
}
|
|
|
|
for (let i = 0; i < vm.obj.items[woItemIndex].units.length; i++) {
|
|
if (vm.obj.items[woItemIndex].units[i].isDirty) {
|
|
//clone and skip viz and other fields
|
|
const o = window.$gz.util.deepCopySkip(
|
|
vm.obj.items[woItemIndex].units[i],
|
|
["uid", "isDirty"]
|
|
);
|
|
const res = await window.$gz.api.upsert(`${API_BASE_URL}items/units`, o);
|
|
if (res.error) {
|
|
handleSaveError(vm, {
|
|
error: res.error,
|
|
itemUid: vm.obj.items[woItemIndex].uid,
|
|
childKey: "units",
|
|
childUid: vm.obj.items[woItemIndex].units[i].uid
|
|
});
|
|
} else {
|
|
//Server will update fields on put or post for most workorder graph objecs so need to update entire object here
|
|
res.data.isDirty = false; //prime isDirty to detect future edits
|
|
res.data.warrantyViz = null; //Add local key so it's reactive in vue
|
|
res.data.uid = i; //set uid again as it's lost in the save
|
|
vm.obj.items[woItemIndex].units.splice(i, 1, res.data);
|
|
}
|
|
}
|
|
}
|
|
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++) {
|
|
if (vm.obj.items[woItemIndex].scheduledUsers[i].isDirty) {
|
|
//clone and skip viz and other fields
|
|
const o = window.$gz.util.deepCopySkip(
|
|
vm.obj.items[woItemIndex].scheduledUsers[i],
|
|
["uid", "isDirty"]
|
|
);
|
|
|
|
const res = await window.$gz.api.upsert(
|
|
`${API_BASE_URL}items/scheduled-users`,
|
|
o
|
|
);
|
|
|
|
if (res.error) {
|
|
handleSaveError(vm, {
|
|
error: res.error,
|
|
itemUid: vm.obj.items[woItemIndex].uid,
|
|
childKey: "scheduledUsers",
|
|
childUid: vm.obj.items[woItemIndex].scheduledUsers[i].uid
|
|
});
|
|
} else {
|
|
//Server will update fields on put or post for most workorder graph objecs so need to update entire object here
|
|
res.data.isDirty = false; //prime isDirty to detect future edits
|
|
res.data.uid = i; //set uid again as it's lost in the save
|
|
vm.obj.items[woItemIndex].scheduledUsers.splice(i, 1, res.data);
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
if (d.id != 0) {
|
|
const res = await window.$gz.api.remove(
|
|
`${API_BASE_URL}items/scheduled-users/${d.id}`
|
|
);
|
|
if (res.error) {
|
|
handleSaveError(vm, {
|
|
error: res.error,
|
|
itemUid: vm.obj.items[woItemIndex].uid,
|
|
childKey: "scheduledUsers",
|
|
childUid: d.uid
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
vm.obj.items[woItemIndex].scheduledUsers.splice(i, 1);
|
|
}
|
|
|
|
//----
|
|
return;
|
|
}
|
|
|
|
/////////////////////////////
|
|
// TASKS
|
|
//
|
|
async function saveTasks(vm, woItemIndex) {
|
|
//DELETE FLAGGED ITEMS FIRST
|
|
await deleteTasks(vm, woItemIndex);
|
|
if (vm.saveResult.fatal) {
|
|
return;
|
|
}
|
|
|
|
for (let i = 0; i < vm.obj.items[woItemIndex].tasks.length; i++) {
|
|
if (vm.obj.items[woItemIndex].tasks[i].isDirty) {
|
|
//clone and skip viz and other fields
|
|
const o = window.$gz.util.deepCopySkip(
|
|
vm.obj.items[woItemIndex].tasks[i],
|
|
["uid", "isDirty"]
|
|
);
|
|
|
|
const res = await window.$gz.api.upsert(`${API_BASE_URL}items/tasks`, o);
|
|
if (res.error) {
|
|
handleSaveError(vm, {
|
|
error: res.error,
|
|
itemUid: vm.obj.items[woItemIndex].uid,
|
|
childKey: "tasks",
|
|
childUid: vm.obj.items[woItemIndex].tasks[i].uid
|
|
});
|
|
} else {
|
|
//Server will update fields on put or post for most workorder graph objecs so need to update entire object here
|
|
res.data.isDirty = false; //prime isDirty to detect future edits
|
|
res.data.uid = i; //set uid again as it's lost in the save
|
|
vm.obj.items[woItemIndex].tasks.splice(i, 1, res.data);
|
|
}
|
|
}
|
|
}
|
|
return; //made it
|
|
}
|
|
|
|
async function deleteTasks(vm, woItemIndex) {
|
|
//walk the array backwards as items may be spliced out
|
|
for (var i = vm.obj.items[woItemIndex].tasks.length - 1; i >= 0; i--) {
|
|
const d = vm.obj.items[woItemIndex].tasks[i];
|
|
if (!d.deleted) {
|
|
continue;
|
|
}
|
|
|
|
if (d.id != 0) {
|
|
const res = await window.$gz.api.remove(
|
|
`${API_BASE_URL}items/tasks/${d.id}`
|
|
);
|
|
if (res.error) {
|
|
handleSaveError(vm, {
|
|
error: res.error,
|
|
itemUid: vm.obj.items[woItemIndex].uid,
|
|
childKey: "tasks",
|
|
childUid: d.uid
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
vm.obj.items[woItemIndex].tasks.splice(i, 1);
|
|
}
|
|
|
|
//----
|
|
return;
|
|
}
|
|
|
|
/////////////////////////////
|
|
// PARTS
|
|
//
|
|
async function deleteParts(vm, woItemIndex) {
|
|
//walk the array backwards as items may be spliced out
|
|
for (var i = vm.obj.items[woItemIndex].parts.length - 1; i >= 0; i--) {
|
|
const d = vm.obj.items[woItemIndex].parts[i];
|
|
if (!d.deleted) {
|
|
continue;
|
|
}
|
|
if (d.id != 0) {
|
|
const res = await window.$gz.api.remove(
|
|
`${API_BASE_URL}items/parts/${d.id}`
|
|
);
|
|
if (res.error) {
|
|
handleSaveError(vm, {
|
|
error: res.error,
|
|
itemUid: vm.obj.items[woItemIndex].uid,
|
|
childKey: "parts",
|
|
childUid: d.uid
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
vm.obj.items[woItemIndex].parts.splice(i, 1);
|
|
}
|
|
|
|
//----
|
|
return;
|
|
}
|
|
|
|
async function saveParts(vm, woItemIndex) {
|
|
//DELETE FLAGGED ITEMS FIRST
|
|
await deleteParts(vm, woItemIndex);
|
|
if (vm.saveResult.fatal) {
|
|
return;
|
|
}
|
|
|
|
for (let i = 0; i < vm.obj.items[woItemIndex].parts.length; i++) {
|
|
if (vm.obj.items[woItemIndex].parts[i].isDirty) {
|
|
//clone and skip viz and other fields
|
|
const o = window.$gz.util.deepCopySkip(
|
|
vm.obj.items[woItemIndex].parts[i],
|
|
["uid", "isDirty"]
|
|
);
|
|
|
|
const res = await window.$gz.api.upsert(`${API_BASE_URL}items/parts`, o);
|
|
if (res.error) {
|
|
//insufficient stock?
|
|
if (res.error.details) {
|
|
const balanceError = res.error.details.find(z => z.error == "2040");
|
|
if (balanceError && balanceError.message) {
|
|
//set the amount requestable so it surfaces in the UI and can be requested
|
|
const balance = window.$gz.util.stringToFloat(balanceError.message);
|
|
if (balance != null || balance != 0) {
|
|
vm.obj.items[woItemIndex].parts[i].requestAmountViz =
|
|
vm.obj.items[woItemIndex].parts[i].quantity - balance;
|
|
}
|
|
}
|
|
}
|
|
|
|
handleSaveError(vm, {
|
|
error: res.error,
|
|
itemUid: vm.obj.items[woItemIndex].uid,
|
|
childKey: "parts",
|
|
childUid: vm.obj.items[woItemIndex].parts[i].uid
|
|
});
|
|
} else {
|
|
//Server will update fields on put or post for most workorder graph objecs so need to update entire object here
|
|
res.data.isDirty = false; //prime isDirty to detect future edits
|
|
res.data.uid = i; //set uid again as it's lost in the save
|
|
vm.obj.items[woItemIndex].parts.splice(i, 1, res.data);
|
|
}
|
|
}
|
|
}
|
|
return; //made it
|
|
}
|
|
|
|
/////////////////////////////
|
|
// PART REQUESTS
|
|
//
|
|
async function deletePartRequests(vm, woItemIndex) {
|
|
//walk the array backwards as items may be spliced out
|
|
for (var i = vm.obj.items[woItemIndex].partRequests.length - 1; i >= 0; i--) {
|
|
const d = vm.obj.items[woItemIndex].partRequests[i];
|
|
if (!d.deleted) {
|
|
continue;
|
|
}
|
|
if (d.id != 0) {
|
|
const res = await window.$gz.api.remove(
|
|
`${API_BASE_URL}items/part-requests/${d.id}`
|
|
);
|
|
if (res.error) {
|
|
handleSaveError(vm, {
|
|
error: res.error,
|
|
itemUid: vm.obj.items[woItemIndex].uid,
|
|
childKey: "partRequests",
|
|
childUid: d.uid
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
vm.obj.items[woItemIndex].partRequests.splice(i, 1);
|
|
}
|
|
|
|
//----
|
|
return;
|
|
}
|
|
|
|
async function savePartRequests(vm, woItemIndex) {
|
|
//DELETE FLAGGED ITEMS FIRST
|
|
await deletePartRequests(vm, woItemIndex);
|
|
if (vm.saveResult.fatal) {
|
|
return;
|
|
}
|
|
|
|
for (let i = 0; i < vm.obj.items[woItemIndex].partRequests.length; i++) {
|
|
if (vm.obj.items[woItemIndex].partRequests[i].isDirty) {
|
|
//clone and skip viz and other fields
|
|
const o = window.$gz.util.deepCopySkip(
|
|
vm.obj.items[woItemIndex].partRequests[i],
|
|
["uid", "isDirty"]
|
|
);
|
|
|
|
const res = await window.$gz.api.upsert(
|
|
`${API_BASE_URL}items/part-requests`,
|
|
o
|
|
);
|
|
if (res.error) {
|
|
handleSaveError(vm, {
|
|
error: res.error,
|
|
itemUid: vm.obj.items[woItemIndex].uid,
|
|
childKey: "partRequests",
|
|
childUid: vm.obj.items[woItemIndex].partRequests[i].uid
|
|
});
|
|
} else {
|
|
//Server will update fields on put or post for most workorder graph objecs so need to update entire object here
|
|
res.data.isDirty = false; //prime isDirty to detect future edits
|
|
res.data.uid = i; //set uid again as it's lost in the save
|
|
vm.obj.items[woItemIndex].partRequests.splice(i, 1, res.data);
|
|
}
|
|
}
|
|
}
|
|
return; //made it
|
|
}
|
|
|
|
/////////////////////////////
|
|
// 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++) {
|
|
if (vm.obj.items[woItemIndex].labors[i].isDirty) {
|
|
//clone and skip viz and other fields
|
|
const o = window.$gz.util.deepCopySkip(
|
|
vm.obj.items[woItemIndex].labors[i],
|
|
["uid", "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: vm.obj.items[woItemIndex].labors[i].uid
|
|
});
|
|
} else {
|
|
//Server will update fields on put or post for most workorder graph objecs so need to update entire object here
|
|
res.data.isDirty = false; //prime isDirty to detect future edits
|
|
res.data.uid = i; //set uid again as it's lost in the save
|
|
vm.obj.items[woItemIndex].labors.splice(i, 1, res.data);
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
if (d.id != 0) {
|
|
const 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
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
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++) {
|
|
if (vm.obj.items[woItemIndex].travels[i].isDirty) {
|
|
//clone and skip viz and other fields
|
|
const o = window.$gz.util.deepCopySkip(
|
|
vm.obj.items[woItemIndex].travels[i],
|
|
["uid", "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: vm.obj.items[woItemIndex].travels[i].uid
|
|
});
|
|
} else {
|
|
//Server will update fields on put or post for most workorder graph objecs so need to update entire object here
|
|
res.data.isDirty = false; //prime isDirty to detect future edits
|
|
res.data.uid = i; //set uid again as it's lost in the save
|
|
vm.obj.items[woItemIndex].travels.splice(i, 1, res.data);
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
if (d.id != 0) {
|
|
const 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
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
|
|
vm.obj.items[woItemIndex].travels.splice(i, 1);
|
|
}
|
|
|
|
//----
|
|
return;
|
|
}
|
|
|
|
/////////////////////////////
|
|
// 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;
|
|
}
|
|
if (d.id != 0) {
|
|
const 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
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
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++) {
|
|
if (vm.obj.items[woItemIndex].expenses[i].isDirty) {
|
|
//clone and skip viz and other fields
|
|
const o = window.$gz.util.deepCopySkip(
|
|
vm.obj.items[woItemIndex].expenses[i],
|
|
["uid", "isDirty"]
|
|
);
|
|
|
|
const 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: vm.obj.items[woItemIndex].expenses[i].uid
|
|
});
|
|
} else {
|
|
//Server will update fields on put or post for most workorder graph objecs so need to update entire object here
|
|
res.data.isDirty = false; //prime isDirty to detect future edits
|
|
res.data.uid = i; //set uid again as it's lost in the save
|
|
vm.obj.items[woItemIndex].expenses.splice(i, 1, res.data);
|
|
}
|
|
}
|
|
}
|
|
return; //made it
|
|
}
|
|
|
|
/////////////////////////////
|
|
// LOANS
|
|
//
|
|
async function deleteLoans(vm, woItemIndex) {
|
|
//walk the array backwards as items may be spliced out
|
|
for (var i = vm.obj.items[woItemIndex].loans.length - 1; i >= 0; i--) {
|
|
const d = vm.obj.items[woItemIndex].loans[i];
|
|
if (!d.deleted) {
|
|
continue;
|
|
}
|
|
if (d.id != 0) {
|
|
const res = await window.$gz.api.remove(
|
|
`${API_BASE_URL}items/loans/${d.id}`
|
|
);
|
|
if (res.error) {
|
|
handleSaveError(vm, {
|
|
error: res.error,
|
|
itemUid: vm.obj.items[woItemIndex].uid,
|
|
childKey: "loans",
|
|
childUid: d.uid
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
|
|
vm.obj.items[woItemIndex].loans.splice(i, 1);
|
|
}
|
|
|
|
//----
|
|
return;
|
|
}
|
|
|
|
async function saveLoans(vm, woItemIndex) {
|
|
//DELETE FLAGGED ITEMS FIRST
|
|
await deleteLoans(vm, woItemIndex);
|
|
if (vm.saveResult.fatal) {
|
|
return;
|
|
}
|
|
|
|
for (let i = 0; i < vm.obj.items[woItemIndex].loans.length; i++) {
|
|
if (vm.obj.items[woItemIndex].loans[i].isDirty) {
|
|
//clone and skip viz and other fields
|
|
const o = window.$gz.util.deepCopySkip(
|
|
vm.obj.items[woItemIndex].loans[i],
|
|
["uid", "isDirty"]
|
|
);
|
|
const res = await window.$gz.api.upsert(`${API_BASE_URL}items/loans`, o);
|
|
if (res.error) {
|
|
handleSaveError(vm, {
|
|
error: res.error,
|
|
itemUid: vm.obj.items[woItemIndex].uid,
|
|
childKey: "loans",
|
|
childUid: vm.obj.items[woItemIndex].loans[i].uid
|
|
});
|
|
} else {
|
|
//Server will update fields on put or post for most workorder graph objecs so need to update entire object here
|
|
res.data.isDirty = false; //prime isDirty to detect future edits
|
|
res.data.uid = i; //set uid again as it's lost in the save
|
|
vm.obj.items[woItemIndex].loans.splice(i, 1, res.data);
|
|
}
|
|
}
|
|
}
|
|
return; //made it
|
|
}
|
|
|
|
/////////////////////////////
|
|
// OUTSIDE SERVICES
|
|
//
|
|
async function deleteOutsideServices(vm, woItemIndex) {
|
|
//walk the array backwards as items may be spliced out
|
|
for (
|
|
var i = vm.obj.items[woItemIndex].outsideServices.length - 1;
|
|
i >= 0;
|
|
i--
|
|
) {
|
|
const d = vm.obj.items[woItemIndex].outsideServices[i];
|
|
if (!d.deleted) {
|
|
continue;
|
|
}
|
|
if (d.id != 0) {
|
|
const res = await window.$gz.api.remove(
|
|
`${API_BASE_URL}items/outside-services/${d.id}`
|
|
);
|
|
if (res.error) {
|
|
handleSaveError(vm, {
|
|
error: res.error,
|
|
itemUid: vm.obj.items[woItemIndex].uid,
|
|
childKey: "outsideServices",
|
|
childUid: d.uid
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
vm.obj.items[woItemIndex].outsideServices.splice(i, 1);
|
|
}
|
|
|
|
//----
|
|
return;
|
|
}
|
|
|
|
async function saveOutsideServices(vm, woItemIndex) {
|
|
//DELETE FLAGGED ITEMS FIRST
|
|
await deleteOutsideServices(vm, woItemIndex);
|
|
if (vm.saveResult.fatal) {
|
|
return;
|
|
}
|
|
|
|
for (let i = 0; i < vm.obj.items[woItemIndex].outsideServices.length; i++) {
|
|
if (vm.obj.items[woItemIndex].outsideServices[i].isDirty) {
|
|
//clone and skip viz and other fields
|
|
const o = window.$gz.util.deepCopySkip(
|
|
vm.obj.items[woItemIndex].outsideServices[i],
|
|
["uid", "isDirty"]
|
|
);
|
|
|
|
const res = await window.$gz.api.upsert(
|
|
`${API_BASE_URL}items/outside-services`,
|
|
o
|
|
);
|
|
if (res.error) {
|
|
handleSaveError(vm, {
|
|
error: res.error,
|
|
itemUid: vm.obj.items[woItemIndex].uid,
|
|
childKey: "outsideServices",
|
|
childUid: vm.obj.items[woItemIndex].outsideServices[i].uid
|
|
});
|
|
} else {
|
|
//Server will update fields on put or post for most workorder graph objecs so need to update entire object here
|
|
res.data.isDirty = false; //prime isDirty to detect future edits
|
|
res.data.uid = i; //set uid again as it's lost in the save
|
|
vm.obj.items[woItemIndex].outsideServices.splice(i, 1, res.data);
|
|
}
|
|
}
|
|
}
|
|
return; //made it
|
|
}
|
|
|
|
//######################################### 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;
|
|
}
|
|
|
|
const 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}`;
|
|
}
|
|
|
|
const 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"
|
|
};
|
|
|
|
vm.saveResult.errors.forEach(z => {
|
|
if (z.error.details != null) {
|
|
z.error.details.forEach(x => {
|
|
const target = errorTargetFromSaveResult(vm, z, x.target);
|
|
|
|
if (target != null) {
|
|
ret.details.push({
|
|
message: x.message,
|
|
error: x.error,
|
|
target: target
|
|
});
|
|
}
|
|
});
|
|
} else {
|
|
ret = z.error;
|
|
}
|
|
});
|
|
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;
|
|
}
|
|
const 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 }
|
|
});
|
|
break;
|
|
case "duplicate":
|
|
m.vm.duplicateDlgTitle = m.vm.$ay.t("DuplicateToWorkOrder");
|
|
m.vm.duplicateTo = "wo";
|
|
m.vm.duplicateDlg = true;
|
|
break;
|
|
case "genquote":
|
|
m.vm.duplicateDlgTitle = m.vm.$ay.t("DuplicateToQuote");
|
|
m.vm.duplicateTo = "quote";
|
|
m.vm.duplicateDlg = true;
|
|
break;
|
|
case "genpm":
|
|
m.vm.duplicateDlgTitle = m.vm.$ay.t("DuplicateToPM");
|
|
m.vm.duplicateTo = "pm";
|
|
m.vm.duplicateDlg = true;
|
|
break;
|
|
case "report":
|
|
{
|
|
const res = await m.vm.$refs.reportSelector.open(
|
|
{
|
|
AType: window.$gz.type.WorkOrder,
|
|
selectedRowIds: [m.vm.obj.id]
|
|
},
|
|
m.id
|
|
);
|
|
if (res == null) {
|
|
return;
|
|
}
|
|
window.$gz.form.setLastReportMenuItem(FORM_KEY, res, m.vm);
|
|
}
|
|
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;
|
|
case "taskgroupslist":
|
|
m.vm.$router.push({
|
|
name: "svc-task-groups"
|
|
});
|
|
break;
|
|
case "quote":
|
|
window.$gz.eventBus.$emit("openobject", {
|
|
type: window.$gz.type.Quote,
|
|
id: m.vm.obj.fromQuoteId
|
|
});
|
|
break;
|
|
case "pm":
|
|
window.$gz.eventBus.$emit("openobject", {
|
|
type: window.$gz.type.PM,
|
|
id: m.vm.obj.fromPMId
|
|
});
|
|
break;
|
|
case "geoview":
|
|
window.$gz.util.viewGeoLocation({
|
|
latitude: m.vm.obj.latitude,
|
|
longitude: m.vm.obj.longitude,
|
|
address: m.vm.obj.address || m.vm.obj.postAddress,
|
|
city: m.vm.obj.city || m.vm.obj.postCity,
|
|
region: m.vm.obj.region || m.vm.obj.postRegion,
|
|
country: m.vm.obj.country || m.vm.obj.postCountry,
|
|
postCode: m.vm.obj.postCode
|
|
});
|
|
break;
|
|
default:
|
|
window.$gz.eventBus.$emit(
|
|
"notify-warning",
|
|
FORM_KEY + "::context click: [" + m.key + "]"
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////
|
|
//
|
|
//
|
|
function generateMenu(vm) {
|
|
const menuOptions = {
|
|
isMain: false,
|
|
readOnly: vm.formState.readOnly,
|
|
hideCoreBizStandardOptions: vm.obj.userIsRestrictedType,
|
|
hideSearch: vm.obj.userIsRestrictedType,
|
|
icon: "$ayiTools",
|
|
title: "WorkOrder",
|
|
helpUrl: "svc-workorders",
|
|
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
|
|
});
|
|
}
|
|
|
|
menuOptions.menuItems.push({
|
|
title: "Report",
|
|
icon: "$ayiFileAlt",
|
|
key: FORM_KEY + ":report",
|
|
vm: vm
|
|
});
|
|
|
|
const lastReport = window.$gz.form.getLastReport(FORM_KEY);
|
|
if (lastReport != null && !vm.obj.userIsRestrictedType) {
|
|
menuOptions.menuItems.push({
|
|
title: lastReport.name,
|
|
notrans: true,
|
|
icon: "$ayiFileAlt",
|
|
key: FORM_KEY + ":report:" + lastReport.id,
|
|
vm: vm
|
|
});
|
|
}
|
|
|
|
if (vm.rights.change && !vm.obj.userIsRestrictedType) {
|
|
menuOptions.menuItems.push({
|
|
title: "New",
|
|
icon: "$ayiPlus",
|
|
key: FORM_KEY + ":new",
|
|
vm: vm
|
|
});
|
|
}
|
|
|
|
if (
|
|
vm.rights.change &&
|
|
vm.$route.params.recordid != 0 &&
|
|
!vm.obj.userIsRestrictedType
|
|
) {
|
|
menuOptions.menuItems.push({
|
|
title: "Duplicate",
|
|
icon: "$ayiClone",
|
|
key: FORM_KEY + ":duplicate",
|
|
vm: vm
|
|
});
|
|
}
|
|
|
|
if (
|
|
vm.rights.change &&
|
|
vm.$route.params.recordid != 0 &&
|
|
!vm.obj.userIsRestrictedType
|
|
) {
|
|
menuOptions.menuItems.push({
|
|
title: "DuplicateToQuote",
|
|
icon: "$ayiPencilAlt",
|
|
key: FORM_KEY + ":genquote",
|
|
vm: vm
|
|
});
|
|
}
|
|
|
|
if (
|
|
vm.rights.change &&
|
|
vm.$route.params.recordid != 0 &&
|
|
!vm.obj.userIsRestrictedType
|
|
) {
|
|
menuOptions.menuItems.push({
|
|
title: "DuplicateToPM",
|
|
icon: "$ayiBusinessTime",
|
|
key: FORM_KEY + ":genpm",
|
|
vm: vm
|
|
});
|
|
}
|
|
|
|
if (
|
|
vm.rights.delete &&
|
|
vm.$route.params.recordid != 0 &&
|
|
!vm.obj.userIsRestrictedType
|
|
) {
|
|
menuOptions.menuItems.push({
|
|
title: "Delete",
|
|
icon: "$ayiTrashAlt",
|
|
surface: false,
|
|
key: FORM_KEY + ":delete",
|
|
vm: vm
|
|
});
|
|
}
|
|
|
|
//--- /show all ---
|
|
|
|
if (!vm.obj.userIsRestrictedType) {
|
|
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({
|
|
title: "TaskGroupList",
|
|
icon: "$ayiTasks",
|
|
key: FORM_KEY + ":taskgroupslist",
|
|
vm: vm
|
|
});
|
|
}
|
|
menuOptions.menuItems.push({
|
|
title: "GeoView",
|
|
icon: "$ayiMapMarked",
|
|
key: FORM_KEY + ":geoview",
|
|
vm: vm
|
|
});
|
|
|
|
menuOptions.menuItems.push({ divider: true, inset: false });
|
|
|
|
let hasFromInsertDivider = false;
|
|
|
|
if (
|
|
vm.obj.fromQuoteId != null &&
|
|
window.$gz.role.canOpen(window.$gz.type.Quote)
|
|
) {
|
|
menuOptions.menuItems.push({
|
|
title: "WorkOrderFromQuoteID",
|
|
icon: "$ayiPencilAlt",
|
|
key: FORM_KEY + ":quote",
|
|
vm: vm
|
|
});
|
|
hasFromInsertDivider = true;
|
|
}
|
|
if (vm.obj.fromPMId != null && window.$gz.role.canOpen(window.$gz.type.PM)) {
|
|
menuOptions.menuItems.push({
|
|
title: "WorkOrderFromPMID",
|
|
icon: "$ayiBusinessTime",
|
|
key: FORM_KEY + ":pm",
|
|
vm: vm
|
|
});
|
|
hasFromInsertDivider = true;
|
|
}
|
|
|
|
if (hasFromInsertDivider) {
|
|
menuOptions.menuItems.push({ divider: true, inset: false });
|
|
}
|
|
|
|
window.$gz.eventBus.$emit("menu-change", menuOptions);
|
|
}
|
|
|
|
//####################################################
|
|
|
|
let JUST_DELETED = false;
|
|
|
|
/////////////////////////////////
|
|
//
|
|
//
|
|
async function initForm(vm) {
|
|
await fetchTranslatedText();
|
|
await window.$gz.formCustomTemplate.get(FORM_CUSTOM_TEMPLATE_KEY, vm);
|
|
await populateSelectionLists(vm);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////
|
|
//
|
|
// Ensures UI translated text is available
|
|
//
|
|
async function fetchTranslatedText() {
|
|
await window.$gz.translation.cacheTranslations([
|
|
"WorkOrder",
|
|
"CopyWiki",
|
|
"CopyAttachments",
|
|
"Customer",
|
|
"WorkOrderSerialNumber",
|
|
"WorkOrderSummary",
|
|
"WorkOrderCloseByDate",
|
|
"Contract",
|
|
"Project",
|
|
"WorkOrderInvoiceNumber",
|
|
"WorkOrderServiceDate",
|
|
"WorkOrderCustomerContactName",
|
|
"WorkOrderCustomerReferenceNumber",
|
|
"WorkOrderInternalReferenceNumber",
|
|
"WorkOrderOnsite",
|
|
"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",
|
|
"WorkOrderItemCustom1",
|
|
"WorkOrderItemCustom2",
|
|
"WorkOrderItemCustom3",
|
|
"WorkOrderItemCustom4",
|
|
"WorkOrderItemCustom5",
|
|
"WorkOrderItemCustom6",
|
|
"WorkOrderItemCustom7",
|
|
"WorkOrderItemCustom8",
|
|
"WorkOrderItemCustom9",
|
|
"WorkOrderItemCustom10",
|
|
"WorkOrderItemCustom11",
|
|
"WorkOrderItemCustom12",
|
|
"WorkOrderItemCustom13",
|
|
"WorkOrderItemCustom14",
|
|
"WorkOrderItemCustom15",
|
|
"WorkOrderItemCustom16",
|
|
"AddressTypePhysical",
|
|
"AddressTypePostal",
|
|
"AddressCopyToPostal",
|
|
"AddressCopyToPhysical",
|
|
"Address",
|
|
"AddressPostalDeliveryAddress",
|
|
"AddressPostalCity",
|
|
"AddressPostalStateProv",
|
|
"AddressPostalCountry",
|
|
"AddressPostalPostal",
|
|
"AddressDeliveryAddress",
|
|
"AddressCity",
|
|
"AddressStateProv",
|
|
"AddressCountry",
|
|
"AddressLatitude",
|
|
"AddressLongitude",
|
|
"SelectAlternateAddress",
|
|
"WorkOrderItemStatusList",
|
|
"WorkOrderStatusList",
|
|
"WorkOrderItemPriorityList",
|
|
"WorkOrderItemUnitList",
|
|
"WorkOrderItemUnitNotes",
|
|
"Unit",
|
|
"WorkOrderItemUnitCustom1",
|
|
"WorkOrderItemUnitCustom2",
|
|
"WorkOrderItemUnitCustom3",
|
|
"WorkOrderItemUnitCustom4",
|
|
"WorkOrderItemUnitCustom5",
|
|
"WorkOrderItemUnitCustom6",
|
|
"WorkOrderItemUnitCustom7",
|
|
"WorkOrderItemUnitCustom8",
|
|
"WorkOrderItemUnitCustom9",
|
|
"WorkOrderItemUnitCustom10",
|
|
"WorkOrderItemUnitCustom11",
|
|
"WorkOrderItemUnitCustom12",
|
|
"WorkOrderItemUnitCustom13",
|
|
"WorkOrderItemUnitCustom14",
|
|
"WorkOrderItemUnitCustom15",
|
|
"WorkOrderItemUnitCustom16",
|
|
"WorkOrderItemExpenseChargeAmount",
|
|
"WorkOrderItemExpenseChargeTaxCodeID",
|
|
"WorkOrderItemExpenseChargeToCustomer",
|
|
"WorkOrderItemExpenseDescription",
|
|
"WorkOrderItemExpenseList",
|
|
"WorkOrderItemExpenseName",
|
|
"WorkOrderItemExpenseReimburseUser",
|
|
"WorkOrderItemExpenseTaxPaid",
|
|
"WorkOrderItemExpenseTotalCost",
|
|
"WorkOrderItemExpenseUserID",
|
|
"WorkOrderItemLaborList",
|
|
"WorkOrderItemLaborServiceStartDate",
|
|
"WorkOrderItemLaborServiceStopDate",
|
|
"WorkOrderItemLaborServiceRateQuantity",
|
|
"WorkOrderItemLaborServiceRateID",
|
|
"WorkOrderItemLaborServiceDetails",
|
|
"WorkOrderItemLaborUserID",
|
|
"WorkOrderItemLaborNoChargeQuantity",
|
|
"WorkOrderItemLaborTaxRateSaleID",
|
|
"WorkOrderItemLoanNotes",
|
|
"WorkOrderItemLoanOutDate",
|
|
"WorkOrderItemLoanDueDate",
|
|
"WorkOrderItemLoanReturnDate",
|
|
"WorkOrderItemLoanTaxCodeID",
|
|
"WorkOrderItemLoanUnit",
|
|
"WorkOrderItemLoanQuantity",
|
|
"WorkOrderItemLoanRate",
|
|
"WorkOrderItemLoanList",
|
|
"WorkOrderItemTravelList",
|
|
"WorkOrderItemTravelStartDate",
|
|
"WorkOrderItemTravelStopDate",
|
|
"WorkOrderItemTravelRateQuantity",
|
|
"WorkOrderItemTravelDistance",
|
|
"WorkOrderItemTravelServiceRateID",
|
|
"WorkOrderItemTravelDetails",
|
|
"WorkOrderItemTravelUserID",
|
|
"WorkOrderItemTravelNoChargeQuantity",
|
|
"WorkOrderItemTravelTaxRateSaleID",
|
|
"WorkOrderItemTaskTaskID",
|
|
"WorkOrderItemTaskWorkOrderItemTaskCompletionType",
|
|
"WorkOrderItemTaskUser",
|
|
"WorkOrderItemTaskCompletedDate",
|
|
"WorkOrderItemTasks",
|
|
"WorkOrderItemPartList",
|
|
"WorkOrderItemPartDescription",
|
|
"PurchaseOrderItemSerialNumbers",
|
|
"WorkOrderItemPartPartWarehouseID",
|
|
"WorkOrderItemPartQuantity",
|
|
"WorkOrderItemPartSuggestedQuantity",
|
|
"WorkOrderItemPartRealizeSuggested",
|
|
"WorkOrderItemPartTaxPartSaleID",
|
|
"WorkOrderItemPartPartID",
|
|
"WorkOrderItemPartRequestList",
|
|
"WorkOrderItemPartRequestPartID",
|
|
"WorkOrderItemPartRequestPartWarehouseID",
|
|
"WorkOrderItemPartRequestQuantity",
|
|
"WorkOrderItemPartRequestMore",
|
|
"PurchaseOrder",
|
|
"PurchaseOrderExpectedReceiveDate",
|
|
"PurchaseOrderOrderedDate",
|
|
"WorkOrderItemPartRequestOnOrder",
|
|
"WorkOrderItemPartRequestReceived",
|
|
"OutsideServiceList",
|
|
"WorkOrderItemOutsideServiceNotes",
|
|
"WorkOrderItemOutsideServiceVendorSentToID",
|
|
"WorkOrderItemOutsideServiceVendorSentViaID",
|
|
"WorkOrderItemOutsideServiceRMANumber",
|
|
"WorkOrderItemOutsideServiceTrackingNumber",
|
|
"WorkOrderItemOutsideServiceRepairCost",
|
|
"WorkOrderItemOutsideServiceRepairPrice",
|
|
"WorkOrderItemOutsideServiceShippingCost",
|
|
"WorkOrderItemOutsideServiceShippingPrice",
|
|
"WorkOrderItemOutsideServiceDateSent",
|
|
"WorkOrderItemOutsideServiceDateETA",
|
|
"WorkOrderItemOutsideServiceDateReturned",
|
|
"TaxCode",
|
|
"PartDescription",
|
|
"PartUPC",
|
|
"TaskGroup",
|
|
"TaskGroupList",
|
|
"SaveRecordToProceed",
|
|
"Cost",
|
|
"ListPrice",
|
|
"NetPrice",
|
|
"Price",
|
|
"PriceOverride",
|
|
"Tax",
|
|
"TaxAAmt",
|
|
"TaxBAmt",
|
|
"LineTotal",
|
|
"UnitOfMeasure",
|
|
"WorkOrderItemPart",
|
|
"WorkOrderItemLabor",
|
|
"WorkOrderItemTravel",
|
|
"WorkOrderItemExpense",
|
|
"WorkOrderItemScheduledUser",
|
|
"WorkOrderItemUnit",
|
|
"WorkOrderItemTask",
|
|
"WorkOrderItemLoan",
|
|
"WorkOrderItemOutsideService",
|
|
"CustomerSignature",
|
|
"Name",
|
|
"TechSignature",
|
|
"PartAssemblyList",
|
|
"PartAssembly",
|
|
"PartSerialNumbersAvailable",
|
|
"AddMultipleUnits",
|
|
"CopyToWorkOrder",
|
|
"WorkOrderConvertScheduledUserToLabor",
|
|
"WorkOrderConvertAllScheduledUsersToLabor",
|
|
"AppendTasks",
|
|
"UnitWarrantyInfo",
|
|
"UnitModelLifeTimeWarranty",
|
|
"UnitModelVendorID",
|
|
"UnitModelName",
|
|
"UnitDescription",
|
|
"Warranty",
|
|
"WarrantyExpires",
|
|
"UnitPurchaseFromID",
|
|
"UnitPurchasedDate",
|
|
"UnitReceipt",
|
|
"RecentWorkOrders",
|
|
"WorkOrderGenerateUnit",
|
|
"ApplyUnitContract",
|
|
"WorkOrderFromQuoteID",
|
|
"WorkOrderFromPMID",
|
|
"CustomerServiceRequest",
|
|
"DuplicateToWorkOrder",
|
|
"DuplicateToQuote",
|
|
"DuplicateToPM",
|
|
"NewStatus",
|
|
"CustomerTechNotes",
|
|
"RemoveRoles",
|
|
"SelectRoles"
|
|
]);
|
|
}
|
|
|
|
//////////////////////
|
|
//
|
|
//
|
|
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"
|
|
);
|
|
|
|
await window.$gz.enums.fetchEnumList("LoanUnitRateUnit"); //prefetch
|
|
vm.selectLists.loanUnitRateUnits = window.$gz.enums.getSelectionList(
|
|
"LoanUnitRateUnit"
|
|
);
|
|
//---------
|
|
}
|
|
</script>
|