diff --git a/ayanova/devdocs/todo.txt b/ayanova/devdocs/todo.txt index 44bf98c3..2d852c91 100644 --- a/ayanova/devdocs/todo.txt +++ b/ayanova/devdocs/todo.txt @@ -230,6 +230,8 @@ CURRENTLY DOING: save (and delete), then error handling (move to individual save OVERALL ORDERING AND ERRORS + - WOITEMS NEED A GUARANTEED GOOD INDEX apart from all others + uiindex temporary value guaranteed to be unique (timestamp?) - if woitem is flagged for delete all children should be as well just flag them in the tree when the woitem is flagged toggle children - SOFT DELETE ISSUE: diff --git a/ayanova/src/components/work-order-items.vue b/ayanova/src/components/work-order-items.vue index 86723934..5999c37d 100644 --- a/ayanova/src/components/work-order-items.vue +++ b/ayanova/src/components/work-order-items.vue @@ -165,6 +165,7 @@ export default { methods: { newItem() { let newIndex = this.value.items.length; + this.value.items.push({ id: 0, concurrency: 0, @@ -189,7 +190,8 @@ export default { tasks: [], travels: [], units: [], - outsideServices: [] + outsideServices: [], + uid: Date.now() //guaranteed unique and higher than any prior }); this.$emit("change"); this.selectedRow = [{ index: newIndex }]; diff --git a/ayanova/src/views/svc-workorder.vue b/ayanova/src/views/svc-workorder.vue index fb370f94..e3152d2e 100644 --- a/ayanova/src/views/svc-workorder.vue +++ b/ayanova/src/views/svc-workorder.vue @@ -245,9 +245,9 @@ export default { delete: true } }, - saveError: { + saveResult: { fatal: false, //fatal error, further save is pointless, bail early and report - error: null //contains error object + errors: null //contains error objects from save } }; }, @@ -357,6 +357,9 @@ export default { window.$gz.form.setErrorBoxErrors(vm); } else { vm.obj = res.data; + + vm.obj.items.forEach((z, index) => (z.uid = index)); + console.log("getdata items indexed:", vm.obj.items); //modify the menu as necessary generateMenu(vm); //update which areas are available to user @@ -413,8 +416,8 @@ export default { const isPost = vm.obj.id == 0; //reset error object - this.saveError.fatal = false; - this.saveError.error = null; + this.saveResult.fatal = false; + this.saveResult.errors = null; /* @@ -446,7 +449,7 @@ export default { let stateSaved = false; if (this.obj.isLockedAtServer) { await saveState(vm); - if (!this.saveError.fatal) { + 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) @@ -455,115 +458,37 @@ export default { } //HEADER - if (!this.saveError.fatal && !headerSaved) { + if (!this.saveResult.fatal && !headerSaved) { await saveHeader(vm); } //WOITEMS - if (!this.saveError.fatal) { + 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.saveError.fatal && !stateSaved) { + if (!this.saveResult.fatal && !stateSaved) { await saveState(vm); - if (!this.saveError.fatal) { + if (!this.saveResult.fatal) { updateRights(vm); } } //## ALL PARTIAL UPDATES COMPLETED //handle errors - if (this.saveError.error != null) { + if (this.saveResult.errors != null) { //# FAIL ROUTE - //##### TODO: Move error compilation into saves, no need for this complex rigamarole - //bugbug: details is an array inside so there could be multiple errors for that target so need to iterate it inside the iteration - //bugbug: on a server error there is no details so dont' code to expect it - /*i.e. - { - error: { - code: "2002", - message: "See server log for details", - target: "Server internal error" - } - } - */ - //wouldn't this just be easier to do directly inside the saves themselves?? - //why compile it at all, why not make it directly ready to display at the end, there's no other use for this info really - - vm.formState.serverError = this.saveError.error; + vm.formState.serverError = formErrorFromSaveResult(); window.$gz.form.setErrorBoxErrors(vm); - /* + - - - `Items[${activeWoItemIndex}].scheduledUsers[${activeItemIndex}].estimatedQuantity` - { - "fatal": false, - "error": true, - "header": null, - "states": [], - "items": [ - { - "e": { - "code": "2200", - "details": [ - { - "message": "SAVE TEST ERROR", - "target": "Notes", - "error": "2203" - } - ], - "message": "ErrorAPI2200" - }, - "objectIndex": 2 - } - ], - "scheduledUsers": [ - { - "e": { - "code": "2200", - "details": [ - { - "message": "◈◈ TEST SAVE ERROR ◈◈", - "target": "EstimatedQuantity", - "error": "2203" - } - ], - "message": "ErrorAPI2200" - }, - "objectIndex": 0, - "woItemIndex": 3 - } - ] - } - - - DESTINATION FORMAT EXAMPLE: - error: { - code: "2200", - details: [ - { - message: - "LT:PurchaseOrderReceiptItemQuantityReceivedErrorInvalid", - target: "Items[1].QuantityReceived", - error: "2203" - }, - { - message: - "LT:PurchaseOrderReceiptItemQuantityReceivedErrorInvalid", - target: "Items[3].QuantityReceived", - error: "2203" - } - ], - message: "ErrorAPI2200" - } - } - */ //TODO: If it's a fatal error set accordingly and bail out here // if (err.fatal) { @@ -747,50 +672,12 @@ async function saveState(vm) { } } } -function handleSaveError(e) { - /* - e:{ - fatal:true/false,//override, normally this function would determine this on it's own - error:res.error,//server error - itemIndex:null,//if has parent woitem this is where it's index is set - childKey:"scheduledUsers"/"Items",//name of child collection - childIndex:null,//if it's a child this is the index - } -//old compiledError code - err.items.forEach(z => - compiledError.details.push({ - message: z.e.details[0].message, - error: z.e.details[0].error, - target: `Items[${z.objectIndex}].${z.e.details[0].target}` - }) - ); - - err.scheduledUsers.forEach(z => - compiledError.details.push({ - message: z.e.details[0].message, - error: z.e.details[0].error, - target: `Items[${z.woItemIndex}].scheduledUsers[${z.objectIndex}].${z.e.details[0].target}` - }) - ); - - //Goes into this.saveError.error ultimately - saveError: { - fatal: false, //fatal error, further save is pointless, bail early and report - error: null //contains error object - } - */ - - if (this.error == null) { - this.error = []; - } - - //set Target properly as requried and push error into the error collection -} ///////////////////////////// // ITEMS // async function deleteItems(vm) { + //TODO: MUST OPERATE IN SEQUENCE ORDER TO MATCH VIEW //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]; @@ -799,13 +686,7 @@ async function deleteItems(vm) { } let res = await window.$gz.api.remove(`${API_BASE_URL}items/${d.id}`); if (res.error) { - handleSaveError(res.error, true); - err.items.push({ - e: res.error, - objectIndex: d.objectIndex, - woItemIndex: d.woItemIndex - }); - err.error = true; + handleSaveError({ fatal: true, error: res.error, itemIndex: i }); } else { vm.obj.items.splice(i, 1); } @@ -821,6 +702,7 @@ async function saveItems(vm, err) { } //SAVE WOITEMS + //TODO: MUST OPERATE IN SEQUENCE ORDER TO MATCH VIEW for (let i = 0; i < vm.obj.items.length; i++) { //get copy of item without child collections for independant submit const { @@ -840,8 +722,7 @@ async function saveItems(vm, err) { const isPost = o.id == 0; let res = await window.$gz.api.upsert(`${API_BASE_URL}items`, o); if (res.error) { - err.items.push({ e: res.error, objectIndex: i }); - err.error = true; + handleSaveError({ error: res.error, itemIndex: i }); 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 @@ -885,7 +766,7 @@ async function saveItems(vm, err) { ///////////////////////////// // SCHEDULED USERS // -async function deleteScheduledUsers(vm, woItemIndex, err) { +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; @@ -900,6 +781,13 @@ async function deleteScheduledUsers(vm, woItemIndex, err) { `${API_BASE_URL}items/scheduledusers/${d.id}` ); if (res.error) { + handleSaveError({ + error: res.error, + itemIndex: woItemIndex, + childKey: "scheduledUsers", + childIndex: i + }); + err.scheduledUsers.push({ e: res.error, objectIndex: d.objectIndex, @@ -957,6 +845,137 @@ async function saveScheduledUsers(vm, woItemIndex, err) { //todo: other grandchildren //######################################### UTILITY METHODS ########################################### + +function handleSaveError(e) { + //TODO: decide if fatal here and set accordingly + if (this.errors == null) { + this.errors = []; + } + this.errors.push(e); +} + +function formErrorFromSaveResult() { + //digest saveresult and compile into standard form error and return + + /* + + + + `Items[${activeWoItemIndex}].scheduledUsers[${activeItemIndex}].estimatedQuantity` + { + "fatal": false, + "error": true, + "header": null, + "states": [], + "items": [ + { + "e": { + "code": "2200", + "details": [ + { + "message": "SAVE TEST ERROR", + "target": "Notes", + "error": "2203" + } + ], + "message": "ErrorAPI2200" + }, + "objectIndex": 2 + } + ], + "scheduledUsers": [ + { + "e": { + "code": "2200", + "details": [ + { + "message": "◈◈ TEST SAVE ERROR ◈◈", + "target": "EstimatedQuantity", + "error": "2203" + } + ], + "message": "ErrorAPI2200" + }, + "objectIndex": 0, + "woItemIndex": 3 + } + ] + } + + + DESTINATION FORMAT EXAMPLE: + error: { + code: "2200", + details: [ + { + message: + "LT:PurchaseOrderReceiptItemQuantityReceivedErrorInvalid", + target: "Items[1].QuantityReceived", + error: "2203" + }, + { + message: + "LT:PurchaseOrderReceiptItemQuantityReceivedErrorInvalid", + target: "Items[3].QuantityReceived", + error: "2203" + } + ], + message: "ErrorAPI2200" + } + } + + e:{ + fatal:true/false,//override, normally this function would determine this on it's own + error:res.error,//server error + itemUid:null,//if has parent woitem this is its UID which is used to determine actual index + childKey:"scheduledUsers"/"Items",//name of child collection + childIndex:null,//if it's a child this is the index + } + +//old compiledError code + err.items.forEach(z => + compiledError.details.push({ + message: z.e.details[0].message, + error: z.e.details[0].error, + target: `Items[${z.objectIndex}].${z.e.details[0].target}` + }) + ); + + err.scheduledUsers.forEach(z => + compiledError.details.push({ + message: z.e.details[0].message, + error: z.e.details[0].error, + target: `Items[${z.woItemIndex}].scheduledUsers[${z.objectIndex}].${z.e.details[0].target}` + }) + ); + + //Goes into this.saveResult.errors ultimately + saveResult: { + fatal: false, //fatal error, further save is pointless, bail early and report + error: null //contains error object + } + + + + //bugbug: details is an array inside so there could be multiple errors for that target so need to iterate it inside the iteration + //bugbug: on a server error there is no details so dont' code to expect it + /*i.e. + { + error: { + code: "2002", + message: "See server log for details", + target: "Server internal error" + } + } + */ + //TODO: compile is back on the menu + //iterate saveresult errors and hydrate into a displayable error as expected + //(deal with translate uid's to woitem indexes that still exist) + */ + ////set Target properly as requried and push error into the error collection + // this.saveResult.errors; +} + ///////////////////////////// // //