Files
raven-client/ayanova/src/components/work-order-items.vue
2021-06-02 22:59:46 +00:00

871 lines
28 KiB
Vue

<template>
<div v-if="value != null" class="mt-8">
<v-row>
<!-- Title and menu -->
<v-col cols="12">
<v-menu offset-y>
<template v-slot:activator="{ on, attrs }">
<div class="text-h5">
<v-icon x-large :color="hasData ? 'primary' : null" class="mr-2"
>$ayiWrench</v-icon
>
{{ $ay.t("WorkOrderItemList") }}
<v-btn large icon v-bind="attrs" v-on="on">
<v-icon small color="primary">$ayiEllipsisV</v-icon>
</v-btn>
</div>
</template>
<v-list>
<v-list-item v-if="canAdd" @click="newItem">
<v-list-item-icon>
<v-icon>$ayiPlus</v-icon>
</v-list-item-icon>
<v-list-item-title>{{ $ay.t("New") }}</v-list-item-title>
</v-list-item>
<v-list-item v-if="canDelete && !isDeleted" @click="deleteItem">
<v-list-item-icon>
<v-icon>$ayiTrashAlt</v-icon>
</v-list-item-icon>
<v-list-item-title>{{ $ay.t("SoftDelete") }}</v-list-item-title>
</v-list-item>
<v-list-item v-if="canDelete && isDeleted" @click="unDeleteItem">
<v-list-item-icon>
<v-icon>$ayiTrashRestoreAlt</v-icon>
</v-list-item-icon>
<v-list-item-title>{{ $ay.t("Undelete") }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</v-col>
<template v-if="hasData">
<!-- ################################ WORK ORDER ITEMS TABLE ############################### -->
<v-col cols="12" class="mb-10">
<v-data-table
:headers="headerList"
:items="itemList"
item-key="index"
v-model="selectedRow"
class="elevation-1"
disable-pagination
disable-filtering
disable-sort
hide-default-footer
data-cy="itemsTable"
dense
:item-class="itemRowClasses"
@click:row="handleRowClick"
:show-select="$vuetify.breakpoint.xs"
single-select
>
<template v-slot:[`item.status`]="{ item }">
<template v-if="item.status.id != null">
<v-icon class="mr-3" :color="item.status.color"
>$ayiCircle</v-icon
>{{ item.status.name }}
</template>
</template>
<template v-slot:[`item.priority`]="{ item }">
<template v-if="item.priority.id != null">
<v-icon class="mr-3" :color="item.priority.color"
>$ayiFireAlt</v-icon
>{{ item.priority.name }}
</template>
</template>
<template v-slot:[`item.warranty`]="{ item }">
<v-simple-checkbox
v-model="item.warranty"
disabled
></v-simple-checkbox>
</template>
</v-data-table>
</v-col>
</template>
<template v-if="hasData && hasSelection">
<v-btn
v-if="canDelete && isDeleted"
large
@click="unDeleteItem"
color="primary"
>{{ $ay.t("Undelete")
}}<v-icon right large>$ayiTrashRestoreAlt</v-icon></v-btn
>
<v-col
v-if="form().showMe(this, 'Items.WorkOrderItemSummary')"
cols="12"
>
<v-textarea
v-model="value.items[activeItemIndex].notes"
:readonly="formState.readOnly"
:disabled="isDeleted"
:label="$ay.t('WorkOrderItemSummary')"
:error-messages="
form().serverErrors(this, `items[${activeItemIndex}].notes`)
"
:ref="`items[${activeItemIndex}].notes`"
:rules="[form().required(this, `items[${activeItemIndex}].notes`)]"
data-cy="Items.Notes"
@input="fieldValueChanged(`items[${activeItemIndex}].notes`)"
auto-grow
></v-textarea>
</v-col>
<v-col
v-if="form().showMe(this, 'WorkOrderItemSequence')"
cols="12"
sm="6"
lg="4"
xl="3"
>
<v-text-field
v-model="value.items[activeItemIndex].sequence"
:readonly="formState.readOnly"
:disabled="isDeleted"
:label="$ay.t('Sequence')"
:ref="`items[${activeItemIndex}].sequence`"
:rules="[
form().integerValid(this, `items[${activeItemIndex}].sequence`)
]"
:error-messages="
form().serverErrors(this, `items[${activeItemIndex}].sequence`)
"
@input="fieldValueChanged(`items[${activeItemIndex}].sequence`)"
type="number"
></v-text-field>
</v-col>
<v-col v-if="form().showMe(this, 'WorkOrderItemTechNotes')" cols="12">
<v-textarea
v-model="value.items[activeItemIndex].techNotes"
:readonly="formState.readOnly"
:disabled="isDeleted"
:label="$ay.t('WorkOrderItemTechNotes')"
:error-messages="
form().serverErrors(this, `items[${activeItemIndex}].techNotes`)
"
:ref="`items[${activeItemIndex}].techNotes`"
data-cy="items.techNotes"
@input="fieldValueChanged(`items[${activeItemIndex}].techNotes`)"
auto-grow
></v-textarea>
</v-col>
<v-col
v-if="form().showMe(this, 'WorkOrderItemRequestDate')"
cols="12"
sm="6"
lg="4"
xl="3"
>
<gz-date-time-picker
:label="$ay.t('WorkOrderItemRequestDate')"
v-model="value.items[activeItemIndex].requestDate"
:readonly="formState.readOnly"
:disabled="isDeleted"
:ref="`items[${activeItemIndex}].requestDate`"
data-cy="requestDate"
:error-messages="
form().serverErrors(this, `items[${activeItemIndex}].requestDate`)
"
@input="fieldValueChanged('requestDate')"
></gz-date-time-picker>
</v-col>
<v-col
v-if="form().showMe(this, 'WorkOrderItemWorkOrderStatusID')"
cols="12"
sm="6"
lg="4"
xl="3"
>
<v-autocomplete
v-model="value.items[activeItemIndex].workorderItemStatusId"
:readonly="formState.readOnly"
:disabled="isDeleted"
@input="fieldValueChanged('workorderItemStatusId')"
:items="selectableStatusList"
item-text="name"
item-value="id"
:label="$ay.t('WorkOrderItemWorkOrderStatusID')"
:error-messages="
form().serverErrors(
this,
`items[${activeItemIndex}].workorderItemStatusId`
)
"
:ref="`items[${activeItemIndex}].workorderItemStatusId`"
data-cy="workorderItemStatusId"
prepend-icon="$ayiEdit"
@click:prepend="handleEditItemStatusClick()"
>
<template v-slot:selection="{ item }">
<v-icon class="mr-3" :color="item.color">$ayiCircle</v-icon
>{{ item.name }}
</template>
<template v-slot:item="{ item }">
<v-list-item-avatar>
<v-icon :color="item.color">$ayiCircle</v-icon>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title
><span
:class="
item.active
? ''
: 'disabled--text text-decoration-line-through'
"
>{{ item.name }}</span
></v-list-item-title
>
<v-list-item-subtitle> {{ item.notes }}</v-list-item-subtitle>
</v-list-item-content>
</template>
</v-autocomplete>
</v-col>
<v-col
v-if="form().showMe(this, 'WorkOrderItemPriorityID')"
cols="12"
sm="6"
lg="4"
xl="3"
>
<v-autocomplete
v-model="value.items[activeItemIndex].workorderItemPriorityId"
:readonly="formState.readOnly"
:disabled="isDeleted"
@input="fieldValueChanged('workorderItemPriorityId')"
:items="selectablePriorityList"
item-text="name"
item-value="id"
:label="$ay.t('WorkOrderItemPriorityID')"
:error-messages="
form().serverErrors(
this,
`items[${activeItemIndex}].workorderItemPriorityId`
)
"
:ref="`items[${activeItemIndex}].workorderItemPriorityId`"
data-cy="workorderItemPriorityId"
prepend-icon="$ayiEdit"
@click:prepend="handleEditItemPriorityClick()"
>
<template v-slot:selection="{ item }">
<v-icon class="mr-3" :color="item.color">$ayiFireAlt</v-icon
>{{ item.name }}
</template>
<template v-slot:item="{ item }">
<v-list-item-avatar>
<v-icon :color="item.color">$ayiFireAlt</v-icon>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title
><span
:class="
item.active
? ''
: 'disabled--text text-decoration-line-through'
"
>{{ item.name }}</span
></v-list-item-title
>
</v-list-item-content>
</template>
</v-autocomplete>
</v-col>
<v-col
v-if="form().showMe(this, 'WorkOrderItemWarrantyService')"
cols="12"
sm="6"
lg="4"
xl="3"
>
<v-checkbox
v-model="value.items[activeItemIndex].warrantyService"
:readonly="formState.readOnly"
:disabled="isDeleted"
:label="$ay.t('WorkOrderItemWarrantyService')"
:ref="`items[${activeItemIndex}].warrantyService`"
data-cy="warrantyService"
:error-messages="
form().serverErrors(
this,
`items[${activeItemIndex}].warrantyService`
)
"
@change="fieldValueChanged('warrantyService')"
></v-checkbox>
</v-col>
<v-col v-if="form().showMe(this, 'WorkOrderItemTags')" cols="12">
<gz-tag-picker
v-model="value.items[activeItemIndex].tags"
:readonly="formState.readOnly"
:ref="`items[${activeItemIndex}].tags`"
data-cy="tags"
:error-messages="
form().serverErrors(this, `items[${activeItemIndex}].tags`)
"
@input="fieldValueChanged('tags')"
></gz-tag-picker>
</v-col>
<v-col cols="12">
<gz-custom-fields
v-model="value.items[activeItemIndex].customFields"
:form-key="formCustomTemplateKey"
:readonly="formState.readOnly"
:parent-v-m="this"
key-start-with="WorkOrderItemCustom"
:ref="`items[${activeItemIndex}].customFields`"
data-cy="customFields"
:error-messages="
form().serverErrors(
this,
`items[${activeItemIndex}].customFields`
)
"
@input="fieldValueChanged('customFields')"
></gz-custom-fields>
</v-col>
<v-col v-if="form().showMe(this, 'WorkOrderItemWiki')" cols="12">
<gz-wiki
:aya-type="$ay.ayt().WorkOrderItem"
:aya-id="value.id"
:ref="`items[${activeItemIndex}].wiki`"
v-model="value.items[activeItemIndex].wiki"
:readonly="formState.readOnly"
@input="fieldValueChanged('wiki')"
></gz-wiki
></v-col>
<v-col
v-if="form().showMe(this, 'WorkOrderItemAttachments') && value.id"
cols="12"
>
<gz-attachments
:readonly="formState.readOnly"
:aya-type="$ay.ayt().WorkOrderItem"
:aya-id="value.items[activeItemIndex].id"
></gz-attachments
></v-col>
<!-- ############################################################################
GRANDCHILDREN
############################################################################ -->
<v-col
cols="12"
v-if="
pvm.subRights.scheduledUsers.visible &&
form().showMe(this, 'WorkOrderItemUnitList')
"
>
<GzWoItemUnits
v-model="value"
:pvm="pvm"
:active-wo-item-index="activeItemIndex"
data-cy="woItemUnits"
@change="$emit('change')"
/>
</v-col>
<v-col
cols="12"
v-if="
pvm.subRights.scheduledUsers.visible &&
form().showMe(this, 'WorkOrderItemScheduledUserList')
"
>
<GzWoItemScheduledUsers
v-model="value"
:pvm="pvm"
:active-wo-item-index="activeItemIndex"
data-cy="woItemScheduledUsers"
@change="$emit('change')"
/>
</v-col>
<v-col
cols="12"
v-if="
pvm.subRights.tasks.visible &&
form().showMe(this, 'WorkOrderItemTasks')
"
>
<GzWoItemTasks
v-model="value"
:pvm="pvm"
:active-wo-item-index="activeItemIndex"
data-cy="woItemTasks"
@change="$emit('change')"
/>
</v-col>
<v-col
cols="12"
v-if="
pvm.subRights.parts.visible &&
form().showMe(this, 'WorkOrderItemPartList')
"
>
<GzWoItemParts
v-model="value"
:pvm="pvm"
:active-wo-item-index="activeItemIndex"
data-cy="woItemParts"
@change="$emit('change')"
/>
</v-col>
<v-col
cols="12"
v-if="
pvm.useInventory &&
value.items[activeItemIndex].partRequests.length > 0 &&
pvm.subRights.partRequests.visible &&
form().showMe(this, 'WorkOrderItemPartRequestList')
"
>
<GzWoItemPartRequests
v-model="value"
:pvm="pvm"
:active-wo-item-index="activeItemIndex"
data-cy="woItemPartRequests"
@change="$emit('change')"
/>
</v-col>
<v-col
cols="12"
v-if="
pvm.subRights.labors.visible &&
form().showMe(this, 'WorkOrderItemLaborList')
"
>
<GzWoItemLabors
v-model="value"
:pvm="pvm"
:active-wo-item-index="activeItemIndex"
data-cy="woItemLabors"
@change="$emit('change')"
/>
</v-col>
<v-col
cols="12"
v-if="
pvm.subRights.travels.visible &&
form().showMe(this, 'WorkOrderItemTravelList')
"
>
<GzWoItemTravels
v-model="value"
:pvm="pvm"
:active-wo-item-index="activeItemIndex"
data-cy="woItemTravels"
@change="$emit('change')"
/>
</v-col>
<v-col
cols="12"
v-if="
pvm.subRights.expenses.visible &&
form().showMe(this, 'WorkOrderItemExpenseList')
"
>
<GzWoItemExpenses
v-model="value"
:pvm="pvm"
:active-wo-item-index="activeItemIndex"
data-cy="woItemExpenses"
@change="$emit('change')"
/>
</v-col>
<v-col
cols="12"
v-if="
pvm.subRights.expenses.visible &&
form().showMe(this, 'WorkOrderItemLoanList')
"
>
<GzWoItemLoans
v-model="value"
:pvm="pvm"
:active-wo-item-index="activeItemIndex"
data-cy="woItemLoans"
@change="$emit('change')"
/>
</v-col>
<v-col
cols="12"
v-if="
pvm.subRights.expenses.visible &&
form().showMe(this, 'WorkOrderItemOutsideServiceList')
"
>
<GzWoItemOutsideServices
v-model="value"
:pvm="pvm"
:active-wo-item-index="activeItemIndex"
data-cy="woItemOutsideServices"
@change="$emit('change')"
/>
</v-col>
</template>
</v-row>
</div>
</template>
<script>
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/* XXXeslint-disable */
////////////////////////////////////////////////////////////////////////////////////////////////////////////
import GzWoItemUnits from "../components/work-order-item-units.vue";
import GzWoItemScheduledUsers from "../components/work-order-item-scheduled-users.vue";
import GzWoItemLabors from "../components/work-order-item-labors.vue";
import GzWoItemTravels from "../components/work-order-item-travels.vue";
import GzWoItemExpenses from "../components/work-order-item-expenses.vue";
import GzWoItemTasks from "../components/work-order-item-tasks.vue";
import GzWoItemParts from "../components/work-order-item-parts.vue";
import GzWoItemPartRequests from "../components/work-order-item-part-requests.vue";
import GzWoItemLoans from "../components/work-order-item-loans.vue";
import GzWoItemOutsideServices from "../components/work-order-item-outside-services.vue";
export default {
components: {
GzWoItemUnits,
GzWoItemScheduledUsers,
GzWoItemExpenses,
GzWoItemLabors,
GzWoItemTravels,
GzWoItemTasks,
GzWoItemParts,
GzWoItemPartRequests,
GzWoItemLoans,
GzWoItemOutsideServices
},
created() {
this.setDefaultView();
},
data() {
return {
activeItemIndex: null,
selectedRow: [],
test: null
};
},
props: {
value: {
default: null,
type: Object
},
pvm: {
default: null,
type: Object
}
},
methods: {
newItem() {
let newIndex = this.value.items.length;
this.value.items.push({
id: 0,
concurrency: 0,
notes: null,
wiki: null,
customFields: "{}",
tags: [],
workOrderId: this.value.id,
techNotes: null,
workorderItemStatusId: null,
workorderItemPriorityId: null,
requestDate: null,
warrantyService: false,
sequence: newIndex + 1, //indexes are zero based but sequences are visible to user so 1 based
isDirty: true,
expenses: [],
labors: [],
loans: [],
parts: [],
partRequests: [],
scheduledUsers: [],
tasks: [],
travels: [],
units: [],
outsideServices: [],
uid: Date.now() //used for error tracking / display
});
this.$emit("change");
this.selectedRow = [{ index: newIndex }];
this.activeItemIndex = newIndex;
},
unDeleteItem() {
this.value.items[this.activeItemIndex].deleted = false;
//CHILDREN
this.value.items[this.activeItemIndex].scheduledUsers.forEach(
z => (z.deleted = false)
);
//todo: other grandchildren
this.setDefaultView();
},
deleteItem() {
this.value.items[this.activeItemIndex].deleted = true;
//CHILDREN
this.value.items[this.activeItemIndex].scheduledUsers.forEach(
z => (z.deleted = true)
);
//todo: other grandchildren
this.setDefaultView();
this.$emit("change");
},
setDefaultView: function() {
//if only one record left then display it otherwise just let the datatable show what the user can click on
if (this.value && this.value.items && this.value.items.length == 1) {
this.selectedRow = [{ index: 0 }];
this.activeItemIndex = 0;
} else {
this.selectedRow = [];
this.activeItemIndex = null; //select nothing in essence resetting a child selects and this one too clearing form
}
},
handleRowClick: function(item) {
this.activeItemIndex = item.index;
this.selectedRow = [{ index: item.index }];
},
handleEditItemStatusClick: function() {
window.$gz.eventBus.$emit("openobject", {
type: window.$gz.type.WorkOrderItemStatus,
id: this.value.items[this.activeItemIndex].workorderItemStatusId
});
},
handleEditItemPriorityClick: function() {
window.$gz.eventBus.$emit("openobject", {
type: window.$gz.type.WorkOrderItemPriority,
id: this.value.items[this.activeItemIndex].workorderItemPriorityId
});
},
form() {
return window.$gz.form;
},
fieldValueChanged(ref) {
if (!this.formState.loading && !this.formState.readonly) {
//flag this record dirty so it gets picked up by save
this.value.items[this.activeItemIndex].isDirty = true;
window.$gz.form.fieldValueChanged(this.pvm, ref);
}
},
itemRowClasses: function(item) {
let ret = "";
const isDeleted = this.value.items[item.index].deleted === true;
const hasError = this.form().childRowHasError(
this,
`Items[${item.index}].`
);
if (isDeleted) {
ret += this.form().tableRowDeletedClass();
}
if (hasError) {
ret += this.form().tableRowErrorClass();
}
return ret;
}
},
computed: {
isDeleted: function() {
if (this.value.items[this.activeItemIndex] == null) {
this.setDefaultView();
return true;
}
return this.value.items[this.activeItemIndex].deleted === true;
},
headerList: function() {
/*
public uint Concurrency { get; set; }
public string Notes { get; set; }//Was Summary
public string Wiki { get; set; }
public string CustomFields { get; set; }
public List<string> Tags { get; set; } = new List<string>();
[Required]
public long WorkOrderId { get; set; }
public string TechNotes { get; set; }
public long? WorkorderItemStatusId { get; set; }
public long? WorkorderItemPriorityId { get; set; }
public DateTime RequestDate { get; set; }
public bool WarrantyService { get; set; } = false;
IN ORDER: notes, status, date, priority, warranty, tags
except they will not prefill at server but will be set here since the wo differs from the po in that there is no instant update with new viz fields to populate from server
and it's probably not a big list to fill anyway
If the column is a text, left-align it
If the column is a number or number + unit, (or date) right-align it (like excel)
*/
let headers = [];
if (this.form().showMe(this, "WorkOrderItemSequence")) {
headers.push({
text: this.$ay.t("Sequence"),
align: "left",
value: "sequence"
});
}
headers.push({
text: this.$ay.t("WorkOrderItemSummary"), //mandatory not hidden
align: "left",
value: "notes"
});
if (this.form().showMe(this, "WorkOrderItemWorkOrderStatusID")) {
headers.push({
text: this.$ay.t("WorkOrderItemWorkOrderStatusID"),
align: "left",
value: "status"
});
}
if (this.form().showMe(this, "WorkOrderItemRequestDate")) {
headers.push({
text: this.$ay.t("WorkOrderItemRequestDate"),
align: "right",
value: "requestDate"
});
}
if (this.form().showMe(this, "WorkOrderItemPriorityID")) {
headers.push({
text: this.$ay.t("WorkOrderItemPriorityID"),
align: "left",
value: "priority"
});
}
if (this.form().showMe(this, "WorkOrderItemWarrantyService")) {
headers.push({
text: this.$ay.t("WorkOrderItemWarrantyService"),
align: "center",
value: "warranty"
});
}
if (this.form().showMe(this, "WorkOrderItemTags")) {
headers.push({
text: this.$ay.t("Tags"),
align: "left",
value: "tags"
});
}
// headers.push({ text: "", value: "actions" });
return headers;
},
itemList: function() {
return this.value.items
.map((x, i) => {
const stat = statusViz(x.workorderItemStatusId, this);
const prior = priorityViz(x.workorderItemPriorityId, this);
return {
index: i,
id: x.id,
sequence: x.sequence,
notes: x.notes,
status: stat,
requestDate: window.$gz.locale.utcDateToShortDateAndTimeLocalized(
x.requestDate,
this.pvm.timeZoneName,
this.pvm.languageName,
this.pvm.hour12
),
priority: prior,
warranty: x.warrantyService,
tags: x.tags
};
})
.sort((a, b) => a.sequence - b.sequence);
},
selectableStatusList: function() {
const selectedId = this.value.items[this.activeItemIndex]
.workorderItemStatusId;
return this.pvm.selectLists.woItemStatus.filter(s => {
return s.active == true || s.id == selectedId;
});
},
selectablePriorityList: function() {
const selectedId = this.value.items[this.activeItemIndex]
.workorderItemPriorityId;
return this.pvm.selectLists.woItemPriorities.filter(s => {
return s.active == true || s.id == selectedId;
});
},
formState: function() {
return this.pvm.formState;
},
formCustomTemplateKey: function() {
return this.pvm.formCustomTemplateKey;
},
hasData: function() {
return this.value.items.length > 0;
},
hasSelection: function() {
return this.activeItemIndex != null;
},
canAdd: function() {
return (
!this.pvm.formState.readOnly &&
this.pvm.rights.change &&
this.pvm.subRights.items.create
);
},
canDelete: function() {
return (
this.activeItemIndex != null &&
!this.pvm.formState.readOnly &&
this.pvm.rights.change &&
this.pvm.subRights.items.delete
);
}
}
};
//TODO: Function status name from woitemstatusid
//and priority same
//this.pvm.pickLists.woItemStatus.find(x=>x.id==x.workorderItemStatusId)
//////////////////////
//
//
function statusViz(id, vm) {
if (id == null) {
return { name: null, color: null };
}
return vm.pvm.selectLists.woItemStatus.find(s => s.id == id);
}
//////////////////////
//
//
function priorityViz(id, vm) {
if (id == null) {
return { name: null, color: null };
}
return vm.pvm.selectLists.woItemPriorities.find(s => s.id == id);
}
</script>