1063 lines
31 KiB
JavaScript
1063 lines
31 KiB
JavaScript
/* xeslint-disable */
|
|
///////////////////////////////
|
|
// gzform
|
|
//
|
|
// provides form services and utilities
|
|
// validation services
|
|
// dirty and change tracking
|
|
// and also general error display in forms
|
|
//probably should be broken up more
|
|
// All translation keys for validation *MUST* be fetched prior to this being used as it assumes all keys are fetched first
|
|
// Add any new keys used to the block in translation.js=>commonKeysEditForm
|
|
import Vue from "vue";
|
|
|
|
let triggeringChange = false;
|
|
|
|
function isEmpty(o) {
|
|
if (typeof o == "number" && o == 0) {
|
|
return false;
|
|
}
|
|
return !o;
|
|
}
|
|
|
|
////////////////////////////////////
|
|
// isInt value??
|
|
//
|
|
//FROM HERE: https://stackoverflow.com/a/14794066/8939
|
|
//fast test if is an integer:
|
|
function isInt(value) {
|
|
let x;
|
|
if (isNaN(value)) {
|
|
return false;
|
|
}
|
|
x = parseFloat(value);
|
|
// console.log("isInt:", x);
|
|
return (x | 0) === x;
|
|
}
|
|
|
|
////////////////////////////////////
|
|
// isNumber
|
|
//
|
|
//FROM HERE: https://stackoverflow.com/a/1830632/8939
|
|
function isNumber(n) {
|
|
return !isNaN(parseFloat(n)) && isFinite(n);
|
|
}
|
|
|
|
////////////////////////////////////
|
|
// Get control from ref
|
|
//
|
|
function getControl(vm, ref) {
|
|
let ctrl = vm.$refs[ref];
|
|
// if (vm.$ay.dev) {
|
|
// // //NOTE: Due to automatic code formatting some refs will come here with newlines in them resulting in no matches
|
|
// // console.log("ref before is", ref);
|
|
// // let tref = ref.replace(/\s/g, "");
|
|
// // console.log("gzform::getcontrol, ref is ", { tref: tref, ref: ref });
|
|
// if (ctrl == null) {
|
|
// console.log(`gzform::getControl ref ${ref} not found on form`);
|
|
// } else {
|
|
// console.log(`gzform::getControl ref ${ref} *WAS* found on form!`);
|
|
// }
|
|
// }
|
|
return ctrl;
|
|
}
|
|
|
|
////////////////////////////////////
|
|
// Get value from control
|
|
//
|
|
function getControlValue(ctrl) {
|
|
let value = ctrl.value;
|
|
return value;
|
|
}
|
|
|
|
////////////////////////////////////
|
|
// Get field name from control
|
|
//
|
|
function getControlLabel(ctrl) {
|
|
if (ctrl.label == undefined) {
|
|
return "UNKNOWN CONTROL";
|
|
} else {
|
|
return ctrl.label;
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////
|
|
// Get errors for a particular field
|
|
// from server error collection
|
|
//
|
|
function getErrorsForField(vm, ref) {
|
|
//Note: to debug this on forms just put {{ formState.serverError }}
|
|
//on the form to see what is actually stored there and should be showing
|
|
let ret = [];
|
|
if (ref == "generalerror") {
|
|
ret = vm.formState.serverError.details.filter(
|
|
z => z.target == false || z.target == "generalerror"
|
|
);
|
|
} else {
|
|
ret = vm.formState.serverError.details.filter(function(o) {
|
|
if (!o.target) {
|
|
return false;
|
|
}
|
|
//server error fields are capitalized
|
|
//client field names are generally lower case except for custom fields
|
|
//so we need to normalize them all to lower case to match
|
|
//they will always differ by more than case so this is fine
|
|
|
|
//NOTE: Indexed child collection error target field names are in this scheme "Items[2].FieldName"
|
|
//where Items is the name of the parent models property that contains the child collection
|
|
|
|
return o.target.toLowerCase() == ref.toLowerCase();
|
|
});
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
///////////////////////////////
|
|
// ERROR BOX ERRORS
|
|
// gathers any messages for error box on form which is the generic catch all for non field specific errors from server
|
|
// and application itself locally
|
|
function getErrorBoxErrors(vm, errs) {
|
|
let hasErrors = false;
|
|
let ret = "";
|
|
if (errs.length > 0) {
|
|
hasErrors = true;
|
|
//loop array and append each error to a return string
|
|
for (let i = 0; i < errs.length; i++) {
|
|
ret += errs[i] + "\r\n";
|
|
}
|
|
}
|
|
|
|
//any application errors?
|
|
if (vm.formState.appError) {
|
|
hasErrors = true;
|
|
ret += vm.formState.appError + "\r\n----------\r\n" + ret;
|
|
}
|
|
|
|
if (!hasErrors) {
|
|
return null;
|
|
} else {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
export default {
|
|
///////////////////////////////
|
|
// REQUIRED
|
|
//
|
|
required(vm, ref) {
|
|
if (vm.formState.loading) {
|
|
return true;
|
|
}
|
|
|
|
let ctrl = getControl(vm, ref);
|
|
if (typeof ctrl == "undefined") {
|
|
return true;
|
|
}
|
|
|
|
let value = getControlValue(ctrl);
|
|
if (!isEmpty(value)) {
|
|
return true;
|
|
}
|
|
|
|
// "ErrorRequiredFieldEmpty": "{0} is a required field. Please enter a value for {0}",
|
|
let err = vm.$ay.t("ErrorRequiredFieldEmpty");
|
|
let fieldName = getControlLabel(ctrl);
|
|
err = err.replace("{0}", fieldName);
|
|
//replace only replaces first instance so need to do it twice
|
|
err = err.replace("{0}", fieldName);
|
|
//Update the form status
|
|
this.setFormState({
|
|
vm: vm,
|
|
valid: false
|
|
});
|
|
return err;
|
|
},
|
|
|
|
///////////////////////////////
|
|
// MAXLENGTH
|
|
//
|
|
maxLength(vm, ref, max) {
|
|
if (vm.formState.loading) {
|
|
return true;
|
|
}
|
|
let ctrl = getControl(vm, ref);
|
|
if (typeof ctrl == "undefined") {
|
|
return true;
|
|
}
|
|
|
|
let value = getControlValue(ctrl);
|
|
if (isEmpty(value)) {
|
|
return true;
|
|
}
|
|
|
|
if (value.length > max) {
|
|
//get the translated rule text
|
|
// "ErrorFieldLengthExceeded": "{0} can not exceed {1} characters.",
|
|
let err = vm.$ay.t("ErrorFieldLengthExceeded");
|
|
let fieldName = getControlLabel(ctrl);
|
|
err = err.replace("{0}", fieldName);
|
|
err = err.replace("{1}", max);
|
|
//Update the form status
|
|
this.setFormState({
|
|
vm: vm,
|
|
valid: false
|
|
});
|
|
return err;
|
|
} else {
|
|
return true;
|
|
}
|
|
},
|
|
///////////////////////////////
|
|
// MAX 255
|
|
//
|
|
max255(vm, ref) {
|
|
if (vm.formState.loading) {
|
|
return true;
|
|
}
|
|
return this.maxLength(vm, ref, 255);
|
|
},
|
|
///////////////////////////////
|
|
// DatePrecedence
|
|
// (start date must precede end date, however if both are empty then that's ok)
|
|
//
|
|
datePrecedence(vm, refStart, refEnd) {
|
|
if (vm.formState.loading) {
|
|
return true;
|
|
}
|
|
let ctrlStart = getControl(vm, refStart);
|
|
if (typeof ctrlStart == "undefined") {
|
|
return true;
|
|
}
|
|
|
|
let ctrlEnd = getControl(vm, refEnd);
|
|
if (typeof ctrlEnd == "undefined") {
|
|
return true;
|
|
}
|
|
|
|
let valueStart = getControlValue(ctrlStart);
|
|
if (isEmpty(valueStart)) {
|
|
return true;
|
|
}
|
|
|
|
let valueEnd = getControlValue(ctrlEnd);
|
|
if (isEmpty(valueEnd)) {
|
|
return true;
|
|
}
|
|
|
|
valueStart = window.$gz.DateTime.fromISO(valueStart);
|
|
valueEnd = window.$gz.DateTime.fromISO(valueEnd);
|
|
|
|
// if either is not valid.
|
|
//moment.github.io/luxon/docs/manual/validity.html
|
|
if (!valueStart.isValid || !valueEnd.isValid) {
|
|
return true;
|
|
}
|
|
|
|
if (valueStart > valueEnd) {
|
|
// "ErrorStartDateAfterEndDate": "Start date must be earlier than stop / end date",
|
|
let err = vm.$ay.t("ErrorStartDateAfterEndDate");
|
|
//Update the form status
|
|
this.setFormState({
|
|
vm: vm,
|
|
valid: false
|
|
});
|
|
return err;
|
|
} else {
|
|
return true;
|
|
}
|
|
},
|
|
///////////////////////////////
|
|
// Confirm password
|
|
// (two fields must match)
|
|
//
|
|
confirmMatch(vm, refFirst, refSecond) {
|
|
if (vm.formState.loading) {
|
|
return true;
|
|
}
|
|
let ctrlFirst = getControl(vm, refFirst);
|
|
if (typeof ctrlFirst == "undefined") {
|
|
return true;
|
|
}
|
|
|
|
let ctrlSecond = getControl(vm, refSecond);
|
|
if (typeof ctrlSecond == "undefined") {
|
|
return true;
|
|
}
|
|
let valueFirst = getControlValue(ctrlFirst);
|
|
let valueSecond = getControlValue(ctrlSecond);
|
|
|
|
if (valueFirst != valueSecond) {
|
|
let err = vm.$ay.t("ErrorNoMatch");
|
|
//Update the form status
|
|
this.setFormState({
|
|
vm: vm,
|
|
valid: false
|
|
});
|
|
return err;
|
|
} else {
|
|
return true;
|
|
}
|
|
},
|
|
///////////////////////////////
|
|
// INTEGER IS VALID
|
|
//
|
|
integerValid(vm, ref) {
|
|
if (vm.formState.loading) {
|
|
return true;
|
|
}
|
|
let ctrl = getControl(vm, ref);
|
|
if (typeof ctrl == "undefined") {
|
|
return true;
|
|
}
|
|
|
|
let value = getControlValue(ctrl);
|
|
|
|
//this block doesn't work and shouldn't be here afaict
|
|
//if you type a letter for example it presents as empty, I guess because it's set to a numeric input type
|
|
//in any case, empty isn't a valid integer so it should show as a broken rule when a letter or empty is entered
|
|
// if (isEmpty(value)) {
|
|
// return true;
|
|
// }
|
|
|
|
if (isInt(value)) {
|
|
return true;
|
|
}
|
|
|
|
// "ErrorFieldValueNotInteger": "Value must be an integer"
|
|
let err = vm.$ay.t("ErrorFieldValueNotInteger");
|
|
//Update the form status
|
|
this.setFormState({
|
|
vm: vm,
|
|
valid: false
|
|
});
|
|
return err;
|
|
},
|
|
///////////////////////////////
|
|
// DECIMAL
|
|
// Basically anything that can be a number is valid
|
|
//
|
|
decimalValid(vm, ref) {
|
|
if (vm.formState.loading) {
|
|
return true;
|
|
}
|
|
//TODO: Handle commas and spaces in numbers
|
|
//as per window.$gz.translation rules for numbers
|
|
|
|
let ctrl = getControl(vm, ref);
|
|
if (typeof ctrl == "undefined") {
|
|
return true;
|
|
}
|
|
|
|
//DEBUG
|
|
//logControl("decimalValid", ctrl, ref);
|
|
|
|
let value = getControlValue(ctrl);
|
|
if (isEmpty(value)) {
|
|
return true;
|
|
}
|
|
|
|
if (isNumber(value)) {
|
|
return true;
|
|
}
|
|
|
|
// "ErrorFieldValueNotDecimal": "Value must be a number"
|
|
let err = vm.$ay.t("ErrorFieldValueNotDecimal");
|
|
//Update the form status
|
|
this.setFormState({
|
|
vm: vm,
|
|
valid: false
|
|
});
|
|
return err;
|
|
},
|
|
///////////////////////////////
|
|
// MAX VALUE
|
|
// Maximum numeric value
|
|
// maxValue is lt or eq
|
|
// empty is considered valid for this rule
|
|
//
|
|
maxValueValid(vm, ref, maxValue) {
|
|
if (vm.formState.loading) {
|
|
return true;
|
|
}
|
|
|
|
let ctrl = getControl(vm, ref);
|
|
if (typeof ctrl == "undefined") {
|
|
return true;
|
|
}
|
|
|
|
let value = getControlValue(ctrl);
|
|
if (isEmpty(value)) {
|
|
return true;
|
|
}
|
|
|
|
if (!isNumber(value)) {
|
|
return true;
|
|
}
|
|
|
|
//Ok, were here with a non empty number of some kind
|
|
if (value <= maxValue) {
|
|
return true;
|
|
}
|
|
|
|
// "ErrorFieldValueNumberGreaterThanMax": "Value must be less than XX"
|
|
let err = `${vm.$ay
|
|
.t("ErrorFieldValueNumberGreaterThanMax")
|
|
.replace("{0}", maxValue)} ${maxValue}`;
|
|
//Update the form status
|
|
this.setFormState({
|
|
vm: vm,
|
|
valid: false
|
|
});
|
|
return err;
|
|
},
|
|
///////////////////////////////
|
|
// MIN VALUE
|
|
// Minimum numeric value
|
|
// minValue is gt or eq
|
|
// empty is considered valid for this rule
|
|
//
|
|
minValueValid(vm, ref, minValue) {
|
|
if (vm.formState.loading) {
|
|
return true;
|
|
}
|
|
|
|
let ctrl = getControl(vm, ref);
|
|
if (typeof ctrl == "undefined") {
|
|
return true;
|
|
}
|
|
|
|
let value = getControlValue(ctrl);
|
|
if (isEmpty(value)) {
|
|
return true;
|
|
}
|
|
|
|
if (!isNumber(value)) {
|
|
return true;
|
|
}
|
|
|
|
//Ok, were here with a non empty number of some kind
|
|
//actual check
|
|
if (value >= minValue) {
|
|
return true;
|
|
}
|
|
|
|
// "ErrorFieldValueNumberLessThanMin": "Value must be more than XX"
|
|
let err = `${vm.$ay
|
|
.t("ErrorFieldValueNumberLessThanMin")
|
|
.replace("{0}", minValue)} ${minValue}`;
|
|
//Update the form status
|
|
this.setFormState({
|
|
vm: vm,
|
|
valid: false
|
|
});
|
|
return err;
|
|
},
|
|
///////////////////////////////
|
|
// EMAIL IS VALID-ish
|
|
//https://tylermcginnis.com/validate-email-address-javascript/
|
|
emailValid(vm, ref) {
|
|
if (vm.formState.loading) {
|
|
return true;
|
|
}
|
|
let ctrl = getControl(vm, ref);
|
|
if (typeof ctrl == "undefined") {
|
|
return true;
|
|
}
|
|
|
|
//DEBUG
|
|
//logControl("emailValid", ctrl, ref);
|
|
|
|
let value = getControlValue(ctrl);
|
|
if (isEmpty(value)) {
|
|
return true;
|
|
}
|
|
|
|
if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value) == true) {
|
|
return true;
|
|
}
|
|
|
|
let err = vm.$ay.t("ErrorAPI2203"); //"Invalid value"
|
|
//Update the form status
|
|
this.setFormState({
|
|
vm: vm,
|
|
valid: false
|
|
});
|
|
return err;
|
|
},
|
|
///////////////////////////////
|
|
// USER REQUIRED FIELDS
|
|
// (Fields defined by AyaNova users as required on form that are not stock required already)
|
|
// (was using this in testing on widget form notes field but not sure where else it's applicable)
|
|
userRequiredFields(vm, ref, formCustomTemplateFieldName) {
|
|
if (vm.formState.loading) {
|
|
return true;
|
|
}
|
|
|
|
let template =
|
|
window.$gz.store.state.formCustomTemplate[vm.formCustomTemplateKey];
|
|
if (template === undefined) {
|
|
return true;
|
|
}
|
|
//See if control formCustomTemplateFieldName is in server required fields collection
|
|
//this is a collection of both custom field definitions and standard form fields that are required
|
|
//since all names are unique can just filter out the one we need by name which will inherently ignore custom fields by default
|
|
//de-lodash
|
|
// let templateItem = window.$gz. _.find(template, [
|
|
// "fld",
|
|
// formCustomTemplateFieldName
|
|
// ]);
|
|
|
|
let templateItem = template.find(z => z.fld == formCustomTemplateFieldName);
|
|
|
|
//templateItem.required
|
|
if (templateItem === undefined || templateItem.required !== true) {
|
|
return true;
|
|
}
|
|
|
|
let ctrl = getControl(vm, ref);
|
|
if (typeof ctrl == "undefined") {
|
|
return true;
|
|
}
|
|
|
|
let value = getControlValue(ctrl);
|
|
if (!isEmpty(value)) {
|
|
return true;
|
|
}
|
|
|
|
// "ErrorRequiredFieldEmpty": "{0} is a required field. Please enter a value for {0}",
|
|
let err = vm.$ay.t("ErrorRequiredFieldEmpty");
|
|
let fieldName = getControlLabel(ctrl);
|
|
err = err.replace("{0}", fieldName);
|
|
//replace only replaces first instance so need to do it twice
|
|
err = err.replace("{0}", fieldName);
|
|
//Update the form status
|
|
this.setFormState({
|
|
vm: vm,
|
|
valid: false
|
|
});
|
|
return err;
|
|
},
|
|
///////////////////////////////
|
|
// CUSTOMFIELDS
|
|
// For now the only rule is that they can be required or not
|
|
//
|
|
customFieldsCheck(vm, templateItem, subvm, fieldName) {
|
|
//templateItem sample
|
|
// dataKey: "c2"
|
|
// fld: "WidgetCustom2"
|
|
// hide: "false"
|
|
// required: true
|
|
// type: "text"
|
|
// debugger;
|
|
|
|
if (vm.formState.loading) {
|
|
return true;
|
|
}
|
|
|
|
if (templateItem.required !== true) {
|
|
return true;
|
|
}
|
|
|
|
let value = subvm.GetValueForField(templateItem.dataKey);
|
|
if (!isEmpty(value)) {
|
|
return true;
|
|
}
|
|
|
|
//It's empty and it's required so return error
|
|
// "ErrorRequiredFieldEmpty": "{0} is a required field. Please enter a value for {0}",
|
|
let err = vm.$ay.t("ErrorRequiredFieldEmpty");
|
|
err = err.replace("{0}", fieldName);
|
|
//replace only replaces first instance so need to do it twice
|
|
err = err.replace("{0}", fieldName);
|
|
//Update the form status
|
|
this.setFormState({
|
|
vm: vm,
|
|
valid: false
|
|
});
|
|
return err;
|
|
},
|
|
///////////////////////////////
|
|
// SERVER ERRORS
|
|
// Process and return server errors if any for form and field specified
|
|
// note that this is called in turn by every control on the form so it's only job
|
|
// is to return errors if they exist for *that* field
|
|
// Not to be confused with validation errors through the "rules" property
|
|
// that is separate and any errors returned here are *added* to the validation
|
|
// errors in the UI by Vuetify
|
|
//
|
|
serverErrors(vm, ref) {
|
|
let ret = [];
|
|
|
|
//check for errors if we have any errors
|
|
if (!window.$gz.util.objectIsEmpty(vm.formState.serverError)) {
|
|
//de-lodash
|
|
//First let's get the top level error code
|
|
|
|
let apiErrorCode = parseInt(vm.formState.serverError.code);
|
|
//Not all server errors mean the form is invalid, exceptions here
|
|
let formValid = false;
|
|
|
|
/*
|
|
These errors are not the user's fault and no changes to the form are required
|
|
so they may be temporary and user should be able to retry save
|
|
API_CLOSED = 2000,
|
|
API_OPS_ONLY = 2001,
|
|
API_SERVER_ERROR = 2002,
|
|
VALIDATION_REFERENTIAL_INTEGRITY = 2208
|
|
*/
|
|
switch (apiErrorCode) {
|
|
case 2000:
|
|
case 2001:
|
|
case 2002:
|
|
case 2208:
|
|
formValid = true; //we came here because the user saved because the form was valid so it's safe to set that the same again
|
|
break;
|
|
default:
|
|
formValid = false;
|
|
}
|
|
|
|
//GENERAL ERROR
|
|
if (ref == "generalerror") {
|
|
//Add any general errors to ret (specific detail errors for the "generalerror" will be processed later below)
|
|
let err = vm.$ay.t("ErrorAPI" + apiErrorCode.toString());
|
|
|
|
if (vm.formState.serverError.message) {
|
|
err = err + "\r\n" + vm.formState.serverError.message;
|
|
}
|
|
|
|
//Update the form status
|
|
this.setFormState({
|
|
vm: vm,
|
|
valid: formValid
|
|
});
|
|
ret.push(err);
|
|
}
|
|
|
|
//DETAIL ERRORS
|
|
//{"error":{"code":"2200","details":[{"message":"Exception: Error converting value \"\" to type 'AyaNova.Biz.AUTHORIZATION_ROLES'. Path 'roles', line 1, position 141.","target":"roles","error":"2203"}],"message":"Object did not pass validation"}}
|
|
//Specific field validation errors are in an array in "details" key
|
|
if (!window.$gz.util.objectIsEmpty(vm.formState.serverError.details)) {
|
|
//See if this key is in the details array
|
|
let errorsForField = getErrorsForField(vm, ref);
|
|
if (errorsForField.length > 0) {
|
|
//iterate the errorsForField object and add each to return array of errors
|
|
//de-lodash
|
|
//window.$gz. _.each(errorsForField, function(ve) {
|
|
errorsForField.forEach(function(ve) {
|
|
let fldErr = "";
|
|
let fldErrorCode = parseInt(ve.error);
|
|
fldErr =
|
|
vm.$ay.t("ErrorAPI" + fldErrorCode.toString()) +
|
|
" [" +
|
|
ve.error +
|
|
"]";
|
|
if (ve.message) {
|
|
//NOTE: call sync version here as can't call async code from here
|
|
//so translations must already be pre-fetched to work here
|
|
fldErr +=
|
|
' - "' +
|
|
window.$gz.translation.translateStringWithMultipleKeys(
|
|
ve.message
|
|
) +
|
|
'"';
|
|
ret.push(fldErr);
|
|
} else {
|
|
ret.push(fldErr);
|
|
}
|
|
});
|
|
|
|
//Update the form status
|
|
this.setFormState({
|
|
vm: vm,
|
|
valid: false
|
|
});
|
|
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
//default if no error message to display
|
|
return ret;
|
|
},
|
|
///////////////////////////////
|
|
// childRowHasError
|
|
// returns true if error exists for row
|
|
// else returns false
|
|
// (actual errors not returned just for row indicator,
|
|
// user opens child edit form to see exact error)
|
|
//
|
|
childRowHasError(vm, path) {
|
|
//Note: this just shows server errors, not local form validation errors
|
|
//it's assumed user will fix in form or when they submit see the error come back
|
|
//Note: this method is easily converted to return actual errors if it ever makes sense to do that but for now I'm ok with row TTM
|
|
|
|
//No server errors?
|
|
if (window.$gz.util.objectIsEmpty(vm.formState.serverError)) {
|
|
//nothing to process
|
|
return null;
|
|
}
|
|
|
|
//no detail errors?
|
|
if (window.$gz.util.objectIsEmpty(vm.formState.serverError.details)) {
|
|
//nothing to process
|
|
return null;
|
|
}
|
|
|
|
path = path.toLowerCase();
|
|
//Might be an error, check if collectionName is in error collection
|
|
//this is what we're dealing with
|
|
// { "code": "2200", "details": [ { "message": "LT:PurchaseOrderReceiptItemQuantityReceivedErrorInvalid", "target": "Items[0].QuantityReceived", "error": "2203" } ], "message": "ErrorAPI2200" }
|
|
//or multilayered like this: target: "Items[3].scheduledUsers[1].EstimatedQuantity"
|
|
//so set "path" accordingly easy peasy, such error, so wow
|
|
|
|
//filter in items that start with the row collection name and index provided
|
|
return vm.formState.serverError.details.some(function(o) {
|
|
if (!o.target) {
|
|
return false;
|
|
}
|
|
return o.target.toLowerCase().includes(path);
|
|
});
|
|
},
|
|
///////////////////////////////
|
|
// childRowErrorClass
|
|
// returns class to set on row if error exists for row
|
|
// else returns nothing
|
|
//( called by some forms but notably not the workorder or PO which have this built in)
|
|
//
|
|
childRowErrorClass(vm, collectionName, rowIndex) {
|
|
if (this.childRowHasError(vm, collectionName, rowIndex)) {
|
|
return "font-weight-black font-italic error--text";
|
|
}
|
|
return null;
|
|
},
|
|
///////////////////////////////
|
|
// ShowMe
|
|
// (returns false if the field has been set to hidden by the user in the formcustomtemplate)
|
|
// NOTE: that in a form this should only be used with non stock-required fields, if they are already required they cannot be hidden
|
|
//
|
|
showMe(vm, formCustomTemplateFieldName) {
|
|
//special check for wiki field
|
|
//if read only then can't add any content and if no content then no reason to show it at all
|
|
if (formCustomTemplateFieldName == "wiki") {
|
|
if (
|
|
vm.formState.readOnly &&
|
|
(vm.obj.wiki == null || vm.obj.wiki.length == 0)
|
|
) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const template =
|
|
window.$gz.store.state.formCustomTemplate[vm.formCustomTemplateKey];
|
|
|
|
if (template === undefined) {
|
|
return true;
|
|
}
|
|
//See if control templateFieldName is in server required fields collection
|
|
//this is a collection of both custom field definitions and standard form fields that are required
|
|
//since all names are unique can just filter out the one we need by name which will inherently ignore custom fields by default
|
|
|
|
const templateItem = template.find(
|
|
z => z.fld.toLowerCase() == formCustomTemplateFieldName.toLowerCase()
|
|
);
|
|
|
|
if (templateItem === undefined || templateItem.hide !== true) {
|
|
return true;
|
|
}
|
|
|
|
//Only here if we have a record in the custom template for this particular field and it's set to hide:true
|
|
return false;
|
|
},
|
|
|
|
///////////////////////////////
|
|
// ClearformState.serverErrors
|
|
// Clear all server errors and app errors and ensure error box doesn't show
|
|
//
|
|
deleteAllErrorBoxErrors(vm) {
|
|
//clear all keys from server error
|
|
window.$gz.util.removeAllPropertiesFromObject(vm.formState.serverError);
|
|
//clear app errors
|
|
vm.formState.appError = null;
|
|
//clear out actual message box display
|
|
vm.formState.errorBoxMessage = null;
|
|
//Update the form status
|
|
this.setFormState({
|
|
vm: vm,
|
|
valid: true
|
|
});
|
|
},
|
|
///////////////////////////////
|
|
// setErrorBoxErrors
|
|
// Gather server errors and set the appropriate keys
|
|
//
|
|
setErrorBoxErrors(vm) {
|
|
let errs = this.serverErrors(vm, "generalerror");
|
|
let ret = getErrorBoxErrors(vm, errs);
|
|
vm.formState.errorBoxMessage = ret;
|
|
},
|
|
///////////////////////////////
|
|
// On fieldValueChanged handler
|
|
// formReference is an optional string name of the form ref property if alternative named form
|
|
// - Clear server errrors for this field
|
|
// - Flag form dirty
|
|
// - check and flag form validity
|
|
//
|
|
//
|
|
fieldValueChanged(vm, ref, formReference) {
|
|
let that = this;
|
|
let formControl = null;
|
|
if (formReference == undefined) {
|
|
formControl = vm.$refs.form;
|
|
} else {
|
|
//NOTE: Due to automatic code formatting some refs may come here with newlines in them resulting in no matches
|
|
formReference = formReference.replace(/\s/g, "");
|
|
formControl = vm.$refs[formReference];
|
|
}
|
|
|
|
//NOTE: Due to automatic code formatting some refs may come here with newlines in them resulting in no matches
|
|
// ref = ref.replace(/\s/g, "");
|
|
|
|
//dev error on form?
|
|
if (formControl == null) {
|
|
if (vm.$ay.dev) {
|
|
//not necessarily an error, can happen during form init
|
|
console.trace(
|
|
`gzform::fieldValueChanged formControl is not found ref:${ref}, formReferences:${formReference} `
|
|
);
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
//this is currently required to ensure that this method runs after all the broken rule checks have settled
|
|
Vue.nextTick(function() {
|
|
//-------------
|
|
if (triggeringChange || vm.formState.loading) {
|
|
return;
|
|
}
|
|
|
|
//# REMOVE SERVER ERRORS FOR THIS FIELD REF
|
|
let targetRef = ref.toLowerCase();
|
|
|
|
//NOTE: This block of code is meant to remove all detailed server errors where the Target matches the current referenced control
|
|
//Then it checks to see if there is anything left in details as this might have been all there was and if so removes that whole thing
|
|
//leaving only errors for other fields or nothing if this ref field was all the errors left
|
|
|
|
//Remove any server errors that are for our target ref field
|
|
//and also set a flag if there *are* any server errors for our target field
|
|
let targetFieldHasServerError = false;
|
|
|
|
if (vm.formState.serverError.details) {
|
|
let i = vm.formState.serverError.details.length;
|
|
//iterate backwards so we can mutate the array in place
|
|
while (i--) {
|
|
var o = vm.formState.serverError.details[i];
|
|
if (o.target && o.target.toLowerCase() == targetRef) {
|
|
//remove it, it's for our ref field
|
|
vm.formState.serverError.details.splice(i, 1);
|
|
targetFieldHasServerError = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
//# CLEAN UP SERVER ERRORS IF NONE LEFT
|
|
//If there are no more errors in details then remove the whole thing as it's no longer required
|
|
if (
|
|
vm.formState.serverError.details &&
|
|
vm.formState.serverError.details.length < 1
|
|
) {
|
|
if (vm.formState.serverError.code == "2200") {
|
|
//clear all keys from server error
|
|
window.$gz.util.removeAllPropertiesFromObject(
|
|
vm.formState.serverError
|
|
);
|
|
}
|
|
}
|
|
|
|
//# CLEAR OUT STALE VALIDATION ERRORS FOR CONTROL
|
|
//Clear out old validation display in form by forcing the control's data to change
|
|
//I tried calling form validate and reset but it did nothing
|
|
//probably because it has safeguards to prevent excess validation, this works though so far
|
|
//I added the triggering change guard but it actually doesn't seem to be required here, more investigation is required
|
|
|
|
//TODO: find a cleaner way to remove old validation on the control, why can't it just be set on the control referenced??
|
|
|
|
if (targetFieldHasServerError) {
|
|
triggeringChange = true;
|
|
let val = vm.obj[ref];
|
|
vm.obj[ref] = null;
|
|
vm.obj[ref] = val;
|
|
triggeringChange = false;
|
|
}
|
|
|
|
//# UPDATE FORM STATUS
|
|
let formValid = formControl.validate();
|
|
that.setFormState({
|
|
vm: vm,
|
|
dirty: true,
|
|
valid: formValid
|
|
});
|
|
//---------------
|
|
}); //next tick end
|
|
},
|
|
|
|
setFormState(newState) {
|
|
//this returns a promise so any function that needs to wait for this can utilize that
|
|
|
|
return Vue.nextTick(function() {
|
|
if (newState.valid != null) {
|
|
newState.vm.formState.valid = newState.valid;
|
|
}
|
|
|
|
if (newState.dirty != null) {
|
|
newState.vm.formState.dirty = newState.dirty;
|
|
}
|
|
|
|
if (newState.loading != null) {
|
|
newState.vm.formState.loading = newState.loading;
|
|
}
|
|
|
|
if (newState.readOnly != null) {
|
|
newState.vm.formState.readOnly = newState.readOnly;
|
|
}
|
|
|
|
if (newState.ready != null) {
|
|
newState.vm.formState.ready = newState.ready;
|
|
}
|
|
});
|
|
},
|
|
////////////////////////////////////
|
|
// Get form settings
|
|
// for form specified or empty object if there is none
|
|
// EAch form is responsible for what it stores and how it initializes, this just provides that
|
|
// the form does the actual work of what settings it requires
|
|
// Form settings are temp and saved, saved ones go into vuex and localstorage and persist a refresh
|
|
// and temporary ones are stored in session storage and don't persist a refresh
|
|
//
|
|
getFormSettings(formKey) {
|
|
let formSettings = {
|
|
temp: JSON.parse(sessionStorage.getItem(formKey)),
|
|
saved: window.$gz.store.state.formSettings[formKey]
|
|
};
|
|
|
|
return formSettings;
|
|
},
|
|
////////////////////////////////////
|
|
// Set form settings
|
|
// for form key specified
|
|
// requires object with one or both keys {temp:{...tempformsettings...},saved:{...persistedformsettings...}}
|
|
//
|
|
setFormSettings(formKey, formSettings) {
|
|
// if (window.$gz.dev) {
|
|
// if (!formSettings.saved && !formSettings.temp) {
|
|
// throw new Error(
|
|
// "gzform:setFormSettings - saved AND temp keys are both missing from form data!"
|
|
// );
|
|
// }
|
|
// }
|
|
|
|
if (formSettings.saved) {
|
|
window.$gz.store.commit("setFormSettings", {
|
|
formKey: formKey,
|
|
formSettings: formSettings.saved
|
|
});
|
|
}
|
|
|
|
if (formSettings.temp) {
|
|
sessionStorage.setItem(formKey, JSON.stringify(formSettings.temp));
|
|
}
|
|
}, ////////////////////////////////////
|
|
// Get last report used from form settings
|
|
//
|
|
getLastReport(formKey) {
|
|
let fs = window.$gz.store.state.formSettings[formKey];
|
|
if (fs == null || fs.lastReport == null) {
|
|
return null;
|
|
}
|
|
return fs.lastReport;
|
|
}, ////////////////////////////////////
|
|
// Set last report used in form settings
|
|
//
|
|
setLastReport(formKey, reportSelected) {
|
|
let fs = window.$gz.store.state.formSettings[formKey];
|
|
if (fs == null || fs.lastReport == null) {
|
|
fs = {};
|
|
}
|
|
fs.lastReport = reportSelected;
|
|
|
|
window.$gz.store.commit("setFormSettings", {
|
|
formKey: formKey,
|
|
formSettings: fs
|
|
});
|
|
}, ////////////////////////////////////
|
|
// Add no selection item
|
|
// Used by forms that need the option of an unselected
|
|
// item in a pick list
|
|
//
|
|
addNoSelectionItem(listArray, nullNotZero) {
|
|
if (listArray == undefined || listArray == null) {
|
|
listArray = [];
|
|
}
|
|
listArray.unshift({ name: "-", id: nullNotZero ? null : 0 });
|
|
},
|
|
////////////////////////////////////
|
|
// Get no selection item
|
|
// Used by forms that need just the
|
|
// unselected item itself not added
|
|
// to a list
|
|
//
|
|
getNoSelectionItem(nullNotZero) {
|
|
return { name: "-", id: nullNotZero ? null : 0 };
|
|
},
|
|
////////////////////////////////////
|
|
// Get validity of referenced control
|
|
//
|
|
controlIsValid(vm, ref) {
|
|
// //NOTE: Due to automatic code formatting some refs will come here with newlines in them resulting in no matches
|
|
// ref = ref.replace(/\s/g, "");
|
|
if (vm.$refs[ref]) {
|
|
return vm.$refs[ref].valid;
|
|
}
|
|
return false;
|
|
},
|
|
////////////////////////////////////////
|
|
// All controls are valid?
|
|
//
|
|
controlsAreAllValid(vm, refs) {
|
|
//if any are not valid return false
|
|
for (let i = 0; i < refs.length; i++) {
|
|
let item = refs[i];
|
|
if (vm.$refs[item]) {
|
|
if (!vm.$refs[item].valid) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
},
|
|
////////////////////////////////////////
|
|
// Standard object not found handler
|
|
//
|
|
handleObjectNotFound(vm) {
|
|
window.$gz.eventBus.$emit("notify-error", vm.$ay.t("ErrorAPI2010"));
|
|
// navigate backwards after small delay to show error
|
|
//(the navigate removes the toast notification immediately)
|
|
setTimeout(function() {
|
|
vm.$router.go(-1);
|
|
}, 2000);
|
|
},
|
|
////////////////////////////////////////
|
|
// Standard data table row error class
|
|
//
|
|
tableRowErrorClass() {
|
|
return "font-weight-black font-italic error--text ";
|
|
},
|
|
////////////////////////////////////////
|
|
// Standard data table deleted class
|
|
//
|
|
tableRowDeletedClass() {
|
|
return "text-decoration-line-through ";
|
|
}
|
|
};
|