Add "generic" Employee tab feature, for profile view
i.e. this now exposes a way to begin/end employment status for a person, and invokes the "employment handler" accordingly
This commit is contained in:
parent
c87a452471
commit
e5d5850327
|
@ -2,7 +2,8 @@
|
||||||
<%inherit file="/master/view.mako" />
|
<%inherit file="/master/view.mako" />
|
||||||
|
|
||||||
<%def name="page_content()">
|
<%def name="page_content()">
|
||||||
<profile-info></profile-info>
|
<profile-info @change-content-title="changeContentTitle">
|
||||||
|
</profile-info>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="render_this_page()">
|
<%def name="render_this_page()">
|
||||||
|
@ -179,9 +180,207 @@
|
||||||
% endif
|
% endif
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="render_this_page_template()">
|
<%def name="render_employee_tab_template()">
|
||||||
${parent.render_this_page_template()}
|
<script type="text/x-template" id="employee-tab-template">
|
||||||
|
<div>
|
||||||
|
<div style="display: flex; justify-content: space-between;">
|
||||||
|
|
||||||
|
<div style="flex-grow: 1;">
|
||||||
|
|
||||||
|
<div v-if="employee.exists">
|
||||||
|
|
||||||
|
<b-field horizontal label="Employee ID">
|
||||||
|
<span>{{ employee.id }}</span>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field horizontal label="Employee Status">
|
||||||
|
<span>{{ employee.current ? "current" : "former" }}</span>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field horizontal label="Start Date">
|
||||||
|
<span>{{ employee.startDate }}</span>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field horizontal label="End Date">
|
||||||
|
<span>{{ employee.endDate }}</span>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<p><strong>Employee History</strong></p>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<b-table :data="employeeHistory.data">
|
||||||
|
<template slot-scope="props">
|
||||||
|
|
||||||
|
<b-table-column field="start_date" label="Start Date">
|
||||||
|
{{ props.row.start_date }}
|
||||||
|
</b-table-column>
|
||||||
|
|
||||||
|
<b-table-column field="end_date" label="End Date">
|
||||||
|
{{ props.row.end_date }}
|
||||||
|
</b-table-column>
|
||||||
|
|
||||||
|
% if request.has_perm('people_profile.edit_employee_history'):
|
||||||
|
<b-table-column field="actions" label="Actions">
|
||||||
|
<a href="#" @click.prevent="editEmployeeHistory(props.row)">
|
||||||
|
<i class="fas fa-edit"></i>
|
||||||
|
Edit
|
||||||
|
</a>
|
||||||
|
</b-table-column>
|
||||||
|
% endif
|
||||||
|
|
||||||
|
</template>
|
||||||
|
</b-table>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p v-if="!employee.exists">
|
||||||
|
${person} has never been an employee.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="buttons">
|
||||||
|
|
||||||
|
% if request.has_perm('people_profile.toggle_employee'):
|
||||||
|
|
||||||
|
<b-button v-if="!employee.current"
|
||||||
|
type="is-primary"
|
||||||
|
@click="showStartEmployee()">
|
||||||
|
${person} is now an Employee
|
||||||
|
</b-button>
|
||||||
|
|
||||||
|
<b-button v-if="employee.current"
|
||||||
|
type="is-primary"
|
||||||
|
@click="showStopEmployeeDialog = true">
|
||||||
|
${person} is no longer an Employee
|
||||||
|
</b-button>
|
||||||
|
|
||||||
|
<b-modal has-modal-card
|
||||||
|
:active.sync="showStartEmployeeDialog">
|
||||||
|
<div class="modal-card">
|
||||||
|
|
||||||
|
<header class="modal-card-head">
|
||||||
|
<p class="modal-card-title">Employee Start</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="modal-card-body">
|
||||||
|
<b-field label="Employee Number">
|
||||||
|
<b-input v-model="employeeID"></b-input>
|
||||||
|
</b-field>
|
||||||
|
<b-field label="Start Date">
|
||||||
|
<tailbone-datepicker v-model="employeeStartDate"></tailbone-datepicker>
|
||||||
|
</b-field>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer class="modal-card-foot">
|
||||||
|
<b-button @click="showStartEmployeeDialog = false">
|
||||||
|
Cancel
|
||||||
|
</b-button>
|
||||||
|
<once-button type="is-primary"
|
||||||
|
@click="startEmployee()"
|
||||||
|
:disabled="!employeeStartDate"
|
||||||
|
text="Save">
|
||||||
|
</once-button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</b-modal>
|
||||||
|
|
||||||
|
<b-modal has-modal-card
|
||||||
|
:active.sync="showStopEmployeeDialog">
|
||||||
|
<div class="modal-card">
|
||||||
|
|
||||||
|
<header class="modal-card-head">
|
||||||
|
<p class="modal-card-title">Employee End</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="modal-card-body">
|
||||||
|
<b-field label="End Date"
|
||||||
|
:type="employeeEndDate ? null : 'is-danger'">
|
||||||
|
<tailbone-datepicker v-model="employeeEndDate"></tailbone-datepicker>
|
||||||
|
</b-field>
|
||||||
|
<b-field label="Revoke Internal App Access">
|
||||||
|
<b-checkbox v-model="employeeRevokeAccess">
|
||||||
|
</b-checkbox>
|
||||||
|
</b-field>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer class="modal-card-foot">
|
||||||
|
<b-button @click="showStopEmployeeDialog = false">
|
||||||
|
Cancel
|
||||||
|
</b-button>
|
||||||
|
<once-button type="is-primary"
|
||||||
|
@click="endEmployee()"
|
||||||
|
:disabled="!employeeEndDate"
|
||||||
|
text="Save">
|
||||||
|
</once-button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</b-modal>
|
||||||
|
% endif
|
||||||
|
|
||||||
|
% if request.has_perm('people_profile.edit_employee_history'):
|
||||||
|
<b-modal has-modal-card
|
||||||
|
:active.sync="showEditEmployeeHistoryDialog">
|
||||||
|
<div class="modal-card">
|
||||||
|
|
||||||
|
<header class="modal-card-head">
|
||||||
|
<p class="modal-card-title">Edit Employee History</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="modal-card-body">
|
||||||
|
<b-field label="Start Date">
|
||||||
|
<tailbone-datepicker v-model="employeeHistoryStartDate"></tailbone-datepicker>
|
||||||
|
</b-field>
|
||||||
|
<b-field label="End Date">
|
||||||
|
<tailbone-datepicker v-model="employeeHistoryEndDate"
|
||||||
|
:disabled="!employeeHistoryEndDateRequired">
|
||||||
|
</tailbone-datepicker>
|
||||||
|
</b-field>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer class="modal-card-foot">
|
||||||
|
<b-button @click="showEditEmployeeHistoryDialog = false">
|
||||||
|
Cancel
|
||||||
|
</b-button>
|
||||||
|
<once-button type="is-primary"
|
||||||
|
@click="saveEmployeeHistory()"
|
||||||
|
:disabled="!employeeHistoryStartDate || (employeeHistoryEndDateRequired && !employeeHistoryEndDate)"
|
||||||
|
text="Save">
|
||||||
|
</once-button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</b-modal>
|
||||||
|
% endif
|
||||||
|
|
||||||
|
% if request.has_perm('employees.view'):
|
||||||
|
<b-button v-if="employee.viewURL"
|
||||||
|
tag="a" :href="employee.viewURL">
|
||||||
|
View Employee
|
||||||
|
</b-button>
|
||||||
|
% endif
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="render_employee_tab()">
|
||||||
|
<b-tab-item label="Employee"
|
||||||
|
icon-pack="fas"
|
||||||
|
:icon="employee.current ? 'check' : null">
|
||||||
|
<employee-tab :employee="employee"
|
||||||
|
:employee-history="employeeHistory"
|
||||||
|
@change-content-title="changeContentTitle">
|
||||||
|
</employee-tab>
|
||||||
|
</b-tab-item>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="render_profile_info_template()">
|
||||||
<script type="text/x-template" id="profile-info-template">
|
<script type="text/x-template" id="profile-info-template">
|
||||||
<div>
|
<div>
|
||||||
<b-tabs v-model="activeTab" type="is-boxed">
|
<b-tabs v-model="activeTab" type="is-boxed">
|
||||||
|
@ -322,98 +521,7 @@
|
||||||
|
|
||||||
${self.render_member_tab()}
|
${self.render_member_tab()}
|
||||||
|
|
||||||
<b-tab-item label="Employee" ${'icon="check" icon-pack="fas"' if employee else ''|n}>
|
${self.render_employee_tab()}
|
||||||
|
|
||||||
% if employee:
|
|
||||||
<div style="display: flex; justify-content: space-between;">
|
|
||||||
|
|
||||||
<div>
|
|
||||||
|
|
||||||
<div class="field-wrapper id">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>ID</label>
|
|
||||||
<div class="field">
|
|
||||||
${employee.id or ''}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field-wrapper display_name">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>Display Name</label>
|
|
||||||
<div class="field">
|
|
||||||
${employee.display_name or ''}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field-wrapper status">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>Status</label>
|
|
||||||
<div class="field">
|
|
||||||
${enum.EMPLOYEE_STATUS.get(employee.status, '')}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
% if employee.phones:
|
|
||||||
% for phone in employee.phones:
|
|
||||||
<div class="field-wrapper">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>Phone Number</label>
|
|
||||||
<div class="field">
|
|
||||||
${phone.number} (type: ${phone.type})
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
% endfor
|
|
||||||
% else:
|
|
||||||
<div class="field-wrapper">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>Phone Number</label>
|
|
||||||
<div class="field">
|
|
||||||
(none on file)
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
% endif
|
|
||||||
|
|
||||||
% if employee.emails:
|
|
||||||
% for email in employee.emails:
|
|
||||||
<div class="field-wrapper">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>Email Address</label>
|
|
||||||
<div class="field">
|
|
||||||
${email.address} (type: ${email.type})
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
% endfor
|
|
||||||
% else:
|
|
||||||
<div class="field-wrapper">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>Email Address</label>
|
|
||||||
<div class="field">
|
|
||||||
(none on file)
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
% endif
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
% if request.has_perm('employees.view'):
|
|
||||||
${h.link_to("View Employee", url('employees.view', uuid=employee.uuid), class_='button')}
|
|
||||||
% endif
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
% else:
|
|
||||||
<p>${person} has never been an employee.</p>
|
|
||||||
% endif
|
|
||||||
</b-tab-item><!-- Employee -->
|
|
||||||
|
|
||||||
<b-tab-item label="User" ${'icon="check" icon-pack="fas"' if person.users else ''|n}>
|
<b-tab-item label="User" ${'icon="check" icon-pack="fas"' if person.users else ''|n}>
|
||||||
% if person.users:
|
% if person.users:
|
||||||
|
@ -477,8 +585,213 @@
|
||||||
</script>
|
</script>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="make_this_page_component()">
|
<%def name="render_this_page_template()">
|
||||||
${parent.make_this_page_component()}
|
${parent.render_this_page_template()}
|
||||||
|
${self.render_employee_tab_template()}
|
||||||
|
${self.render_profile_info_template()}
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="set_employee_data()">
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
let EmployeeData = {
|
||||||
|
exists: ${json.dumps(bool(employee))|n},
|
||||||
|
viewURL: ${json.dumps(employee_view_url)|n},
|
||||||
|
current: ${json.dumps(bool(employee and employee.status == enum.EMPLOYEE_STATUS_CURRENT))|n},
|
||||||
|
startDate: ${json.dumps(six.text_type(employee_history.start_date) if employee_history else None)|n},
|
||||||
|
endDate: ${json.dumps(six.text_type(employee_history.end_date) if employee_history and employee_history.end_date else None)|n},
|
||||||
|
id: ${json.dumps(employee.id if employee else None)|n},
|
||||||
|
}
|
||||||
|
|
||||||
|
let EmployeeHistoryData = {
|
||||||
|
data: ${json.dumps(employee_history_data)|n},
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="declare_employee_tab_vars()">
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
let EmployeeTab = {
|
||||||
|
template: '#employee-tab-template',
|
||||||
|
props: {
|
||||||
|
employee: Object,
|
||||||
|
employeeHistory: Object,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showStartEmployeeDialog: false,
|
||||||
|
employeeID: null,
|
||||||
|
employeeStartDate: null,
|
||||||
|
showStopEmployeeDialog: false,
|
||||||
|
employeeEndDate: null,
|
||||||
|
employeeRevokeAccess: false,
|
||||||
|
showEditEmployeeHistoryDialog: false,
|
||||||
|
employeeHistoryUUID: null,
|
||||||
|
employeeHistoryStartDate: null,
|
||||||
|
employeeHistoryEndDate: null,
|
||||||
|
employeeHistoryEndDateRequired: false,
|
||||||
|
|
||||||
|
## TODO: should find a better way to handle CSRF token
|
||||||
|
csrftoken: ${json.dumps(request.session.get_csrf_token() or request.session.new_csrf_token())|n},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
changeContentTitle(newTitle) {
|
||||||
|
this.$emit('change-content-title', newTitle)
|
||||||
|
},
|
||||||
|
|
||||||
|
% if request.has_perm('people_profile.toggle_employee'):
|
||||||
|
|
||||||
|
showStartEmployee() {
|
||||||
|
this.employeeID = this.employee.id
|
||||||
|
this.showStartEmployeeDialog = true
|
||||||
|
},
|
||||||
|
|
||||||
|
startEmployee() {
|
||||||
|
|
||||||
|
let url = '${url('people.profile_start_employee', uuid=person.uuid)}'
|
||||||
|
|
||||||
|
let params = {
|
||||||
|
id: this.employeeID,
|
||||||
|
start_date: this.employeeStartDate,
|
||||||
|
}
|
||||||
|
|
||||||
|
let headers = {
|
||||||
|
## TODO: should find a better way to handle CSRF token
|
||||||
|
'X-CSRF-TOKEN': this.csrftoken,
|
||||||
|
}
|
||||||
|
|
||||||
|
## TODO: should find a better way to handle CSRF token
|
||||||
|
this.$http.post(url, params, {headers: headers}).then(({ data }) => {
|
||||||
|
if (data.success) {
|
||||||
|
this.startEmployeeSuccess(data)
|
||||||
|
} else {
|
||||||
|
this.$buefy.toast.open({
|
||||||
|
message: "Save failed: " + data.error,
|
||||||
|
type: 'is-danger',
|
||||||
|
duration: 4000, // 4 seconds
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, response => {
|
||||||
|
alert("Unexpected error occurred!")
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
startEmployeeSuccess(data) {
|
||||||
|
this.employee.exists = true
|
||||||
|
this.employee.id = data.employee_id
|
||||||
|
this.employee.viewURL = data.employee_view_url
|
||||||
|
this.employee.current = true
|
||||||
|
this.employee.startDate = data.start_date
|
||||||
|
this.employee.endDate = null
|
||||||
|
this.employeeHistory.data = data.employee_history_data
|
||||||
|
// this.customerNumber = data.customer_number
|
||||||
|
this.employeeEndDate = null
|
||||||
|
this.$emit('change-content-title', data.dynamic_content_title)
|
||||||
|
// this.posTabStale = true
|
||||||
|
this.showStartEmployeeDialog = false
|
||||||
|
},
|
||||||
|
|
||||||
|
endEmployee() {
|
||||||
|
|
||||||
|
let url = '${url('people.profile_end_employee', uuid=person.uuid)}'
|
||||||
|
|
||||||
|
let params = {
|
||||||
|
end_date: this.employeeEndDate,
|
||||||
|
revoke_access: this.employeeRevokeAccess,
|
||||||
|
}
|
||||||
|
|
||||||
|
let headers = {
|
||||||
|
## TODO: should find a better way to handle CSRF token
|
||||||
|
'X-CSRF-TOKEN': this.csrftoken,
|
||||||
|
}
|
||||||
|
|
||||||
|
## TODO: should find a better way to handle CSRF token
|
||||||
|
this.$http.post(url, params, {headers: headers}).then(({ data }) => {
|
||||||
|
if (data.success) {
|
||||||
|
this.endEmployeeSuccess(data)
|
||||||
|
} else {
|
||||||
|
this.$buefy.toast.open({
|
||||||
|
message: "Save failed: " + data.error,
|
||||||
|
type: 'is-danger',
|
||||||
|
duration: 4000, // 4 seconds
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, response => {
|
||||||
|
alert("Unexpected error occurred!")
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
endEmployeeSuccess(data) {
|
||||||
|
this.employee.current = false
|
||||||
|
this.employee.endDate = data.end_date
|
||||||
|
this.employeeHistory.data = data.employee_history_data
|
||||||
|
this.employeeStartDate = null
|
||||||
|
this.$emit('change-content-title', data.dynamic_content_title)
|
||||||
|
// this.memberTabStale = true
|
||||||
|
// this.posTabStale = true
|
||||||
|
this.showStopEmployeeDialog = false
|
||||||
|
},
|
||||||
|
|
||||||
|
% endif
|
||||||
|
|
||||||
|
% if request.has_perm('people_profile.edit_employee_history'):
|
||||||
|
|
||||||
|
editEmployeeHistory(row) {
|
||||||
|
this.employeeHistoryUUID = row.uuid
|
||||||
|
this.employeeHistoryStartDate = row.start_date
|
||||||
|
this.employeeHistoryEndDate = row.end_date
|
||||||
|
this.employeeHistoryEndDateRequired = !!row.end_date
|
||||||
|
this.showEditEmployeeHistoryDialog = true
|
||||||
|
},
|
||||||
|
|
||||||
|
saveEmployeeHistory() {
|
||||||
|
|
||||||
|
let url = '${url('people.profile_edit_employee_history', uuid=person.uuid)}'
|
||||||
|
|
||||||
|
let params = {
|
||||||
|
uuid: this.employeeHistoryUUID,
|
||||||
|
start_date: this.employeeHistoryStartDate,
|
||||||
|
end_date: this.employeeHistoryEndDate,
|
||||||
|
}
|
||||||
|
|
||||||
|
let headers = {
|
||||||
|
## TODO: should find a better way to handle CSRF token
|
||||||
|
'X-CSRF-TOKEN': this.csrftoken,
|
||||||
|
}
|
||||||
|
|
||||||
|
## TODO: should find a better way to handle CSRF token
|
||||||
|
this.$http.post(url, params, {headers: headers}).then(({ data }) => {
|
||||||
|
if (data.success) {
|
||||||
|
this.employee.startDate = data.start_date
|
||||||
|
this.employee.endDate = data.end_date
|
||||||
|
this.employeeHistory.data = data.employee_history_data
|
||||||
|
}
|
||||||
|
this.showEditEmployeeHistoryDialog = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
% endif
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="make_employee_tab_component()">
|
||||||
|
${self.declare_employee_tab_vars()}
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
Vue.component('employee-tab', EmployeeTab)
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="make_profile_info_component()">
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
const ProfileInfo = {
|
const ProfileInfo = {
|
||||||
|
@ -489,8 +802,15 @@
|
||||||
person: ${json.dumps(person_data)|n},
|
person: ${json.dumps(person_data)|n},
|
||||||
customers: ${json.dumps(customers_data)|n},
|
customers: ${json.dumps(customers_data)|n},
|
||||||
members: ${json.dumps(members_data)|n},
|
members: ${json.dumps(members_data)|n},
|
||||||
|
employee: EmployeeData,
|
||||||
|
employeeHistory: EmployeeHistoryData,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
methods: {
|
||||||
|
changeContentTitle(newTitle) {
|
||||||
|
this.$emit('change-content-title', newTitle)
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
Vue.component('profile-info', ProfileInfo)
|
Vue.component('profile-info', ProfileInfo)
|
||||||
|
@ -498,5 +818,23 @@
|
||||||
</script>
|
</script>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
<%def name="modify_this_page_vars()">
|
||||||
|
${parent.modify_this_page_vars()}
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
ThisPage.methods.changeContentTitle = function(newTitle) {
|
||||||
|
this.$emit('change-content-title', newTitle)
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="make_this_page_component()">
|
||||||
|
${parent.make_this_page_component()}
|
||||||
|
${self.set_employee_data()}
|
||||||
|
${self.make_employee_tab_component()}
|
||||||
|
${self.make_profile_info_component()}
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
|
||||||
${parent.body()}
|
${parent.body()}
|
||||||
|
|
|
@ -76,6 +76,14 @@ class View(object):
|
||||||
"""
|
"""
|
||||||
return getattr(self.request, 'rattail_config', None)
|
return getattr(self.request, 'rattail_config', None)
|
||||||
|
|
||||||
|
def get_rattail_app(self):
|
||||||
|
"""
|
||||||
|
Returns the Rattail ``AppHandler`` instance, creating it if necessary.
|
||||||
|
"""
|
||||||
|
if not hasattr(self, 'rattail_app'):
|
||||||
|
self.rattail_app = self.rattail_config.get_app()
|
||||||
|
return self.rattail_app
|
||||||
|
|
||||||
def forbidden(self):
|
def forbidden(self):
|
||||||
"""
|
"""
|
||||||
Convenience method, to raise a HTTP 403 Forbidden exception.
|
Convenience method, to raise a HTTP 403 Forbidden exception.
|
||||||
|
|
|
@ -26,6 +26,8 @@ Person Views
|
||||||
|
|
||||||
from __future__ import unicode_literals, absolute_import
|
from __future__ import unicode_literals, absolute_import
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
|
||||||
import six
|
import six
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from sqlalchemy import orm
|
from sqlalchemy import orm
|
||||||
|
@ -432,6 +434,15 @@ class PeopleView(MasterView):
|
||||||
'view_profile_url': profile_url,
|
'view_profile_url': profile_url,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_context_employee(self, employee):
|
||||||
|
"""
|
||||||
|
Return a dict of context data for the given employee.
|
||||||
|
"""
|
||||||
|
app = self.get_rattail_app()
|
||||||
|
handler = app.get_employment_handler()
|
||||||
|
context = handler.get_context_employee(employee)
|
||||||
|
return context
|
||||||
|
|
||||||
def get_context_employee_history(self, employee):
|
def get_context_employee_history(self, employee):
|
||||||
data = []
|
data = []
|
||||||
if employee:
|
if employee:
|
||||||
|
@ -443,6 +454,104 @@ class PeopleView(MasterView):
|
||||||
})
|
})
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def ensure_customer(self, person):
|
||||||
|
"""
|
||||||
|
Return the `Customer` record for the given person, establishing it
|
||||||
|
first if necessary.
|
||||||
|
"""
|
||||||
|
app = self.get_rattail_app()
|
||||||
|
handler = app.get_clientele_handler()
|
||||||
|
customer = handler.ensure_customer(person)
|
||||||
|
return customer
|
||||||
|
|
||||||
|
def profile_start_employee(self):
|
||||||
|
"""
|
||||||
|
View which will cause the person to start being an employee.
|
||||||
|
"""
|
||||||
|
person = self.get_instance()
|
||||||
|
app = self.get_rattail_app()
|
||||||
|
handler = app.get_employment_handler()
|
||||||
|
|
||||||
|
reason = handler.why_not_begin_employment(person)
|
||||||
|
if reason:
|
||||||
|
return {'error': reason}
|
||||||
|
|
||||||
|
data = self.request.json_body
|
||||||
|
start_date = datetime.datetime.strptime(data['start_date'], '%Y-%m-%d').date()
|
||||||
|
employee = handler.begin_employment(person, start_date,
|
||||||
|
employee_id=data['id'])
|
||||||
|
return self.profile_start_employee_result(employee, start_date)
|
||||||
|
|
||||||
|
def profile_start_employee_result(self, employee, start_date):
|
||||||
|
return {
|
||||||
|
'success': True,
|
||||||
|
'employee': self.get_context_employee(employee),
|
||||||
|
'employee_view_url': self.request.route_url('employees.view', uuid=employee.uuid),
|
||||||
|
'start_date': six.text_type(start_date),
|
||||||
|
'employee_history_data': self.get_context_employee_history(employee),
|
||||||
|
}
|
||||||
|
|
||||||
|
def profile_end_employee(self):
|
||||||
|
"""
|
||||||
|
View which will cause the person to stop being an employee.
|
||||||
|
"""
|
||||||
|
person = self.get_instance()
|
||||||
|
app = self.get_rattail_app()
|
||||||
|
handler = app.get_employment_handler()
|
||||||
|
|
||||||
|
reason = handler.why_not_end_employment(person)
|
||||||
|
if reason:
|
||||||
|
return {'error': reason}
|
||||||
|
|
||||||
|
data = dict(self.request.json_body)
|
||||||
|
end_date = datetime.datetime.strptime(data['end_date'], '%Y-%m-%d').date()
|
||||||
|
employee = handler.get_employee(person)
|
||||||
|
handler.end_employment(employee, end_date,
|
||||||
|
revoke_access=data.get('revoke_access'))
|
||||||
|
return self.profile_end_employee_result(employee, end_date)
|
||||||
|
|
||||||
|
def profile_end_employee_result(self, employee, end_date):
|
||||||
|
return {
|
||||||
|
'success': True,
|
||||||
|
'employee': self.get_context_employee(employee),
|
||||||
|
'employee_view_url': self.request.route_url('employees.view', uuid=employee.uuid),
|
||||||
|
'end_date': six.text_type(end_date),
|
||||||
|
'employee_history_data': self.get_context_employee_history(employee),
|
||||||
|
}
|
||||||
|
|
||||||
|
def profile_edit_employee_history(self):
|
||||||
|
"""
|
||||||
|
AJAX view for updating an employee history record.
|
||||||
|
"""
|
||||||
|
person = self.get_instance()
|
||||||
|
employee = person.employee
|
||||||
|
|
||||||
|
uuid = self.request.json_body['uuid']
|
||||||
|
history = self.Session.query(model.EmployeeHistory).get(uuid)
|
||||||
|
if not history or history not in employee.history:
|
||||||
|
return {'error': "Must specify a valid Employee History record for this Person."}
|
||||||
|
|
||||||
|
# all history records have a start date, so always update that
|
||||||
|
start_date = self.request.json_body['start_date']
|
||||||
|
start_date = datetime.datetime.strptime(start_date, '%Y-%m-%d').date()
|
||||||
|
history.start_date = start_date
|
||||||
|
|
||||||
|
# only update end_date if history already had one
|
||||||
|
if history.end_date:
|
||||||
|
end_date = self.request.json_body['end_date']
|
||||||
|
end_date = datetime.datetime.strptime(end_date, '%Y-%m-%d').date()
|
||||||
|
history.end_date = end_date
|
||||||
|
|
||||||
|
self.Session.flush()
|
||||||
|
current_history = employee.get_current_history()
|
||||||
|
|
||||||
|
return {
|
||||||
|
'success': True,
|
||||||
|
'start_date': six.text_type(current_history.start_date),
|
||||||
|
'end_date': six.text_type(current_history.end_date or ''),
|
||||||
|
'employee_history_data': self.get_context_employee_history(employee),
|
||||||
|
}
|
||||||
|
|
||||||
def make_note_form(self, mode, person):
|
def make_note_form(self, mode, person):
|
||||||
schema = NoteSchema().bind(session=self.Session(),
|
schema = NoteSchema().bind(session=self.Session(),
|
||||||
person_uuid=person.uuid)
|
person_uuid=person.uuid)
|
||||||
|
@ -545,6 +654,7 @@ class PeopleView(MasterView):
|
||||||
permission_prefix = cls.get_permission_prefix()
|
permission_prefix = cls.get_permission_prefix()
|
||||||
route_prefix = cls.get_route_prefix()
|
route_prefix = cls.get_route_prefix()
|
||||||
url_prefix = cls.get_url_prefix()
|
url_prefix = cls.get_url_prefix()
|
||||||
|
instance_url_prefix = cls.get_instance_url_prefix()
|
||||||
model_key = cls.get_model_key()
|
model_key = cls.get_model_key()
|
||||||
model_title = cls.get_model_title()
|
model_title = cls.get_model_title()
|
||||||
|
|
||||||
|
@ -564,6 +674,24 @@ class PeopleView(MasterView):
|
||||||
config.add_view(cls, attr='view_profile', route_name='{}.view_profile'.format(route_prefix),
|
config.add_view(cls, attr='view_profile', route_name='{}.view_profile'.format(route_prefix),
|
||||||
permission='{}.view_profile'.format(permission_prefix))
|
permission='{}.view_profile'.format(permission_prefix))
|
||||||
|
|
||||||
|
# profile - start employee
|
||||||
|
config.add_route('{}.profile_start_employee'.format(route_prefix), '{}/profile/start-employee'.format(instance_url_prefix),
|
||||||
|
request_method='POST')
|
||||||
|
config.add_view(cls, attr='profile_start_employee', route_name='{}.profile_start_employee'.format(route_prefix),
|
||||||
|
permission='people_profile.toggle_employee', renderer='json')
|
||||||
|
|
||||||
|
# profile - end employee
|
||||||
|
config.add_route('{}.profile_end_employee'.format(route_prefix), '{}/profile/end-employee'.format(instance_url_prefix),
|
||||||
|
request_method='POST')
|
||||||
|
config.add_view(cls, attr='profile_end_employee', route_name='{}.profile_end_employee'.format(route_prefix),
|
||||||
|
permission='people_profile.toggle_employee', renderer='json')
|
||||||
|
|
||||||
|
# profile - edit employee history
|
||||||
|
config.add_route('{}.profile_edit_employee_history'.format(route_prefix), '{}/profile/edit-employee-history'.format(instance_url_prefix),
|
||||||
|
request_method='POST')
|
||||||
|
config.add_view(cls, attr='profile_edit_employee_history', route_name='{}.profile_edit_employee_history'.format(route_prefix),
|
||||||
|
permission='people_profile.edit_employee_history', renderer='json')
|
||||||
|
|
||||||
# manage notes from profile view
|
# manage notes from profile view
|
||||||
if cls.manage_notes_from_profile_view:
|
if cls.manage_notes_from_profile_view:
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue