This commit is contained in:
2021-08-19 22:13:21 +00:00
parent a1b321ab38
commit c5ede9d2bb
5 changed files with 723 additions and 1 deletions

View File

@@ -18,6 +18,7 @@ Aug
Unit form should have a viz field for last meter reading that shows only if it's metered Unit form should have a viz field for last meter reading that shows only if it's metered
no delete, write once no delete, write once
must migrate as well must migrate as well
NOTE: need to make a switch during migration v7 has workorderitem id but v8 links to workorderitemunit id so need to fixup that as well
migrate woitem loan not tagging loan units?? migrate woitem loan not tagging loan units??

View File

@@ -577,6 +577,18 @@ export default new Router({
component: () => component: () =>
import(/* webpackChunkName: "svc" */ "./views/svc-unit.vue") import(/* webpackChunkName: "svc" */ "./views/svc-unit.vue")
}, },
{
path: "/svc-meter-readings/:unitid",
name: "meter-readings",
component: () =>
import(/* webpackChunkName: "cust" */ "./views/svc-meter-readings.vue")
},
{
path: "/svc-meter-reading/:recordid",
name: "meter-reading",
component: () =>
import(/* webpackChunkName: "cust" */ "./views/svc-meter-reading.vue")
},
{ {
path: "/svc-unit-models", path: "/svc-unit-models",
name: "svc-unit-models", name: "svc-unit-models",

View File

@@ -0,0 +1,536 @@
<template>
<div>
<gz-report-selector ref="reportSelector"></gz-report-selector>
<!-- {{ formState }} -->
<v-form ref="form" v-if="formState.ready">
<v-row>
<gz-error :error-box-message="formState.errorBoxMessage"></gz-error>
<v-col cols="12">
<gz-pick-list
readonly
v-model="obj.customerId"
:aya-type="ayaTypes().Customer"
show-edit-icon
:label="$ay.t('Customer')"
></gz-pick-list>
</v-col>
<v-col cols="12">
<v-textarea
v-model="obj.notes"
:readonly="formState.readOnly"
:label="$ay.t('CustomerNoteNotes')"
:error-messages="form().serverErrors(this, 'notes')"
ref="notes"
data-cy="notes"
@input="fieldValueChanged('notes')"
auto-grow
:clearable="!formState.readOnly"
></v-textarea>
</v-col>
<v-col cols="12">
<gz-tag-picker
v-model="obj.tags"
:readonly="formState.readOnly"
ref="tags"
data-cy="tags"
:error-messages="form().serverErrors(this, 'tags')"
@input="fieldValueChanged('tags')"
></gz-tag-picker>
</v-col>
<v-col cols="12">
<gz-date-time-picker
:label="$ay.t('CustomerNoteNoteDate')"
v-model="obj.noteDate"
:readonly="formState.readOnly"
ref="noteDate"
data-cy="noteDate"
:error-messages="form().serverErrors(this, 'noteDate')"
@input="fieldValueChanged('noteDate')"
></gz-date-time-picker>
</v-col>
</v-row>
</v-form>
<v-overlay :value="!formState.ready || formState.loading">
<v-progress-circular indeterminate :size="64" />
</v-overlay>
</div>
</template>
<script>
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/* Xeslint-disable */
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
const FORM_KEY = "customer-note-edit";
const API_BASE_URL = "customer-note/";
export default {
async created() {
let vm = this;
try {
await initForm(vm);
vm.rights = window.$gz.role.getRights(window.$gz.type.CustomerNote);
vm.formState.readOnly = !vm.rights.change;
window.$gz.eventBus.$on("menu-click", clickHandler);
//id 0 means create or duplicate to new
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
if (!vm.customername) {
await fetchCustomerName(vm).catch(err => {
window.$gz.errorHandler.handleFormError(err, vm);
});
}
}
} else {
vm.obj.customerId = this.$route.params.customerid;
vm.obj.userId = vm.$store.state.userId;
window.$gz.form.setFormState({
vm: vm,
loading: false
});
}
window.$gz.form.setFormState({
vm: vm,
dirty: false,
valid: true
});
generateMenu(vm);
} 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 {
selectLists: {
usertypes: []
},
customername: undefined,
obj: {
//IMPORTANT NOTE: Fields that are NON NULLABLE in the schema for the table but *are* hideable **MUST** have a default value set here or else there will be no way to save the record
//I.E. Serial, usertype fields
//Also, if it's a non-nullable Enum backed field then it should have a valid selection i.e. not zero if there is no zero
id: 0,
concurrency: 0,
notes: null,
noteDate: window.$gz.locale.nowUTC8601String(),
tags: [],
customerId: undefined,
userId: undefined
},
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.CustomerNote
};
},
//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 && !val.readOnly) {
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 + ":new");
} else {
window.$gz.eventBus.$emit("menu-disable-item", FORM_KEY + ":new");
}
},
deep: true
}
},
methods: {
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 &&
!this.formState.readOnly
) {
window.$gz.form.fieldValueChanged(this, ref);
}
},
async getDataFromApi(recordId) {
let vm = this;
window.$gz.form.setFormState({
vm: vm,
loading: true
});
if (!recordId) {
throw new Error(FORM_KEY + "::getDataFromApi -> Missing recordID!");
}
let url = API_BASE_URL + recordId;
try {
window.$gz.form.deleteAllErrorBoxErrors(vm);
let 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;
//modify the menu as necessary
generateMenu(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 submit() {
let vm = this;
if (vm.canSave == false) {
return;
}
try {
window.$gz.form.setFormState({
vm: vm,
loading: true
});
let url = API_BASE_URL; // + vm.$route.params.recordid;
//clear any errors vm might be around from previous submit
window.$gz.form.deleteAllErrorBoxErrors(vm);
let res = await window.$gz.api.upsert(url, vm.obj);
if (res.error) {
vm.formState.serverError = res.error;
window.$gz.form.setErrorBoxErrors(vm);
} else {
//Logic for detecting if a post or put: if id then it was a post, if no id then it was a put
if (res.data.id) {
//POST - whole new object returned
vm.obj = res.data;
//Change URL to new record
//NOTE: will not cause a page re-render, almost nothing does unless forced with a KEY property or using router.GO()
this.$router.replace({
name: "customer-note-edit",
params: {
recordid: res.data.id,
customerid: res.data.customerId,
obj: res.data // Pass data object to new form
}
});
} else {
//PUT - only concurrency token is returned (**warning, if server changes object other fields then this needs to act more like POST above but is more efficient this way**)
//Handle "put" of an existing record (UPDATE)
vm.obj.concurrency = res.data.concurrency;
}
//Update the form status
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 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: "customer-note-edit",
params: { recordid: 0 }
});
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.CustomerNote }
});
} else {
//general report selector chosen
let res = await m.vm.$refs.reportSelector.open({
AType: window.$gz.type.CustomerNote,
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.CustomerNote }
});
}
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: "$ayiClipboard",
title: "CustomerNoteNotes",
helpUrl: "customer-notes",
formData: {
ayaType: window.$gz.type.CustomerNote,
recordId: vm.$route.params.recordid
},
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
});
}
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);
}
//////////////////////////////////////////////////////////
//
// Ensures UI translated text is available
//
async function fetchTranslatedText(vm) {
await window.$gz.translation.cacheTranslations([
"CustomerNoteNotes",
"CustomerNoteNoteDate"
]);
}
///////////////////////////////////////////////////////////
//
//
async function fetchCustomerName(vm) {
//If customer name is missing then fetch it here to display
//this will happen when this form is directly opened from a search
//or record history etc
let res = await window.$gz.api.get(
`name/${window.$gz.type.Customer}/${vm.obj.customerId}`
);
//We never expect there to be no data here
if (!res.hasOwnProperty("data")) {
return Promise.reject(res);
} else {
vm.customername = res.data;
}
}
</script>

View File

@@ -0,0 +1,172 @@
<template>
<div>
<gz-report-selector ref="reportSelector"></gz-report-selector>
<gz-data-table
ref="gzdatatable"
form-key="meter-reading-list"
data-list-key="MeterReadingDataList"
show-select="false"
:reload="reload"
:client-criteria="clientCriteria"
data-cy="MeterReadingsTable"
:pre-filter-mode="preFilterMode"
>
</gz-data-table>
</div>
</template>
<script>
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/* Xeslint-disable */
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////
const FORM_KEY = "meter-reading-list";
export default {
created() {
this.unitId = parseInt(this.$route.params.unitid);
//REQUIRED NON-OPTIONAL FILTER
this.clientCriteria = this.unitId.toString();
this.preFilterMode = {
icon: "$ayiFan",
id: window.$gz.util.stringToIntOrNull(this.$route.params.unitid),
ayatype: window.$gz.type.MeterReading,
viz: this.$route.params.unitname,
clearable: false
};
this.rights = window.$gz.role.getRights(window.$gz.type.Unit);
window.$gz.eventBus.$on("menu-click", clickHandler);
generateMenu(this);
},
beforeDestroy() {
window.$gz.eventBus.$off("menu-click", clickHandler);
},
data() {
return {
unitId: undefined,
// unitname: undefined,
clientCriteria: undefined,
preFilterMode: null,
rights: window.$gz.role.defaultRightsObject(),
aType: window.$gz.type.MeterReading,
reload: false
};
},
methods: {
ayaTypes: function() {
return window.$gz.type;
}
}
};
/////////////////////////////
//
//
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 "new":
m.vm.$router.push({
name: "unit-note-edit",
params: {
recordid: 0,
unitid: m.vm.unitId
}
});
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.MeterReading }
});
} else {
//general report selector chosen
let res = await m.vm.$refs.reportSelector.open(
m.vm.$refs.gzdatatable.getDataListSelection(
window.$gz.type.MeterReading
)
);
//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.MeterReading }
});
}
break;
default:
window.$gz.eventBus.$emit(
"notify-warning",
FORM_KEY + "::context click: [" + m.key + "]"
);
}
}
}
//////////////////////
//
//
function generateMenu(vm) {
let menuOptions = {
isMain: false,
readOnly: !vm.rights.change,
icon: "$ayiWeight",
title: "UnitMeterReadingList",
helpUrl: "svc-meter-readings",
menuItems: [],
formData: {
ayaType: window.$gz.type.MeterReading
}
};
if (vm.rights.change) {
menuOptions.menuItems.push({
title: "New",
icon: "$ayiPlus",
surface: true,
key: FORM_KEY + ":new",
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
});
}
window.$gz.eventBus.$emit("menu-change", menuOptions);
}
</script>

View File

@@ -1479,7 +1479,8 @@ async function fetchTranslatedText(vm) {
"UnitCustom13", "UnitCustom13",
"UnitCustom14", "UnitCustom14",
"UnitCustom15", "UnitCustom15",
"UnitCustom16" "UnitCustom16",
"UnitMeterReadingList"
]); ]);
} }
</script> </script>