合并企业版代码(未测试,先提交到测试分支)
This commit is contained in:
53
mrp_workorder_hr/static/src/components/pin_popup.js
Normal file
53
mrp_workorder_hr/static/src/components/pin_popup.js
Normal file
@@ -0,0 +1,53 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
const { Component, useState, useExternalListener } = owl;
|
||||
|
||||
const INPUT_KEYS = new Set(['Delete', 'Backspace'].concat('0123456789,'.split('')));
|
||||
|
||||
export class PinPopup extends Component {
|
||||
setup() {
|
||||
super.setup();
|
||||
this.state = useState({ buffer: '' });
|
||||
this.employee = this.props.popupData.employee;
|
||||
|
||||
useExternalListener(window, 'keyup', this._onKeyUp);
|
||||
}
|
||||
|
||||
get inputBuffer() {
|
||||
return this.state.buffer.replace(/./g, '•');
|
||||
}
|
||||
|
||||
sendInput(key) {
|
||||
if (INPUT_KEYS.has(key)) {
|
||||
if (key === 'Delete') {
|
||||
this.state.buffer = '';
|
||||
} else if (key === 'Backspace') {
|
||||
this.state.buffer = this.state.buffer.slice(0, -1);
|
||||
} else {
|
||||
this.state.buffer = this.state.buffer + key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this.props.onClosePopup('PinPopup');
|
||||
}
|
||||
|
||||
async confirm() {
|
||||
await this.props.onPinValidate(this.employee.id, this.state.buffer);
|
||||
this.props.onClosePopup('PinPopup');
|
||||
}
|
||||
|
||||
_onKeyUp(ev) {
|
||||
if (INPUT_KEYS.has(ev.key)) {
|
||||
this.sendInput(ev.key);
|
||||
} else if (ev.key === 'Enter') {
|
||||
this.confirm();
|
||||
} else if (ev.key === 'Escape') {
|
||||
this.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
PinPopup.template = 'mrp_workorder_hr.PinPopup';
|
||||
37
mrp_workorder_hr/static/src/components/pin_popup.xml
Normal file
37
mrp_workorder_hr/static/src/components/pin_popup.xml
Normal file
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
<t t-name="mrp_workorder_hr.PinPopup" owl="1">
|
||||
<div role="dialog">
|
||||
<div class="popup centered text-black">
|
||||
<header class="title">
|
||||
Password ?
|
||||
</header>
|
||||
<div class="popup-input">
|
||||
<span class="highlight"><t t-esc="inputBuffer"/></span>
|
||||
</div>
|
||||
<div class="popup-numpad centered">
|
||||
<button t-on-mousedown.prevent="() => this.sendInput('1')">1</button>
|
||||
<button t-on-mousedown.prevent="() => this.sendInput('2')">2</button>
|
||||
<button t-on-mousedown.prevent="() => this.sendInput('3')">3</button>
|
||||
<br/>
|
||||
<button t-on-mousedown.prevent="() => this.sendInput('4')">4</button>
|
||||
<button t-on-mousedown.prevent="() => this.sendInput('5')">5</button>
|
||||
<button t-on-mousedown.prevent="() => this.sendInput('6')">6</button>
|
||||
<br/>
|
||||
<button t-on-mousedown.prevent="() => this.sendInput('7')">7</button>
|
||||
<button t-on-mousedown.prevent="() => this.sendInput('8')">8</button>
|
||||
<button t-on-mousedown.prevent="() => this.sendInput('9')">9</button>
|
||||
<br/>
|
||||
<button t-on-mousedown.prevent="() => this.sendInput('Delete')">C</button>
|
||||
<button t-on-mousedown.prevent="() => this.sendInput('0')">0</button>
|
||||
<button t-on-mousedown.prevent="() => this.sendInput('Backspace')"><span class="fa fa-long-arrow-left"/></button>
|
||||
<br/>
|
||||
</div>
|
||||
<footer class="footer">
|
||||
<button class="btn cancel text-black" t-on-click="cancel">Cancel</button>
|
||||
<button class="btn confirm text-black" t-on-click="confirm">Ok</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
25
mrp_workorder_hr/static/src/components/popup.js
Normal file
25
mrp_workorder_hr/static/src/components/popup.js
Normal file
@@ -0,0 +1,25 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
const { Component } = owl;
|
||||
|
||||
export class SelectionPopup extends Component {
|
||||
|
||||
get title() {
|
||||
return this.props.popupData.title;
|
||||
}
|
||||
|
||||
get list() {
|
||||
return this.props.popupData.list;
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this.props.onClosePopup('SelectionPopup', true);
|
||||
}
|
||||
|
||||
async selectItem(id) {
|
||||
await this.props.onSelectEmployee(id);
|
||||
this.props.onClosePopup('SelectionPopup');
|
||||
}
|
||||
}
|
||||
|
||||
SelectionPopup.template = 'mrp_workorder_hr.SelectionPopup';
|
||||
18
mrp_workorder_hr/static/src/components/popup.xml
Normal file
18
mrp_workorder_hr/static/src/components/popup.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
<t t-name="mrp_workorder_hr.SelectionPopup" owl="1">
|
||||
<div class="popup popup-selection">
|
||||
<h3 class="title" t-esc="title"/>
|
||||
<div class="selection scrollable-y">
|
||||
<t t-foreach="list" t-as="item" t-key="item.id">
|
||||
<div class="selection-item" t-att-class="{ 'selected': item.isSelected }" t-on-click="() => this.selectItem(item.id)">
|
||||
<t t-esc="item.label" />
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
<footer class="footer">
|
||||
<div class="btn cancel text-uppercase" t-on-click="cancel">Cancel</div>
|
||||
</footer>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
156
mrp_workorder_hr/static/src/components/tablet.js
Normal file
156
mrp_workorder_hr/static/src/components/tablet.js
Normal file
@@ -0,0 +1,156 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { useBus, useService } from "@web/core/utils/hooks";
|
||||
import Tablet from '@mrp_workorder/components/tablet';
|
||||
import { SelectionPopup } from '@mrp_workorder_hr/components/popup';
|
||||
import { WorkingEmployeePopup } from '@mrp_workorder_hr/components/working_employee_popup';
|
||||
import { patch } from 'web.utils';
|
||||
import { PinPopup } from '@mrp_workorder_hr/components/pin_popup';
|
||||
|
||||
const { onMounted } = owl;
|
||||
|
||||
patch(Tablet.prototype, 'mrp_workorder_hr', {
|
||||
setup() {
|
||||
this._super();
|
||||
this.notification = useService("notification");
|
||||
this.popup.SelectionPopup = {
|
||||
isShown: false,
|
||||
data: {},
|
||||
};
|
||||
this.popup.PinPopup = {
|
||||
isShown: false,
|
||||
data: {},
|
||||
};
|
||||
this.popup.WorkingEmployeePopup = {
|
||||
isShown: false,
|
||||
data: {},
|
||||
};
|
||||
this.state.tabletEmployeeIds = [];
|
||||
this.employee = this.props.action.context.employee_id;
|
||||
this.actionRedirect = false;
|
||||
useBus(this.workorderBus, "popupEmployeeManagement", this.popupEmployeeManagement);
|
||||
onMounted(() => this.checkEmployeeLogged());
|
||||
},
|
||||
|
||||
checkEmployeeLogged() {
|
||||
if (this.data.employee_list.length && !this.data.employee && !this.employee) {
|
||||
this.popupAddEmployee();
|
||||
}
|
||||
},
|
||||
// Popup Menu Actions
|
||||
|
||||
popupEmployeeManagement() {
|
||||
this.showPopup({ workorderId: this.workorderId }, 'WorkingEmployeePopup');
|
||||
},
|
||||
|
||||
popupAddEmployee() {
|
||||
const list = this.data.employee_list.filter(e => ! this.data.employee_ids.includes(e.id)).map((employee) => {
|
||||
return {
|
||||
id: employee.id,
|
||||
item: employee,
|
||||
label: employee.name,
|
||||
isSelected: false,
|
||||
};
|
||||
});
|
||||
const title = this.env._t('Change Worker');
|
||||
this.showPopup({ title, list }, 'SelectionPopup');
|
||||
},
|
||||
|
||||
popupEmployeePin(employeeId) {
|
||||
const employee = this.data.employee_list.find(e => e.id === employeeId);
|
||||
this.showPopup({ employee }, 'PinPopup');
|
||||
},
|
||||
|
||||
// Buisness method
|
||||
|
||||
async lockEmployee(employeeId, pin) {
|
||||
const pinValid = await this._checkPin(employeeId, pin);
|
||||
if (! pinValid) {
|
||||
this.actionRedirect = this.lockEmployee;
|
||||
return;
|
||||
}
|
||||
this.render();
|
||||
},
|
||||
|
||||
async startEmployee(employeeId, pin) {
|
||||
const pinValid = await this._checkPin(employeeId, pin);
|
||||
if (! pinValid) {
|
||||
this.actionRedirect = this.startEmployee;
|
||||
return;
|
||||
}
|
||||
this.state.tabletEmployeeIds.push(employeeId);
|
||||
await this.orm.call(
|
||||
'mrp.workorder',
|
||||
'start_employee',
|
||||
[this.workorderId, employeeId],
|
||||
);
|
||||
await this.getState();
|
||||
this.render();
|
||||
},
|
||||
|
||||
async stopEmployee(employeeId, pin) {
|
||||
const pinValid = await this._checkPin(employeeId, pin, false);
|
||||
if (! pinValid) {
|
||||
this.actionRedirect = this.stopEmployee;
|
||||
return;
|
||||
}
|
||||
const index = this.state.tabletEmployeeIds.indexOf(employeeId);
|
||||
this.state.tabletEmployeeIds.slice(index, 1);
|
||||
await this.orm.call(
|
||||
'mrp.workorder',
|
||||
'stop_employee',
|
||||
[this.workorderId, employeeId],
|
||||
);
|
||||
await this.getState();
|
||||
this.render();
|
||||
},
|
||||
|
||||
redirectToAction(employeeId, pin) {
|
||||
this.actionRedirect(employeeId, pin);
|
||||
this.actionRedirect = false;
|
||||
},
|
||||
|
||||
get isBlocked() {
|
||||
let isBlocked = this._super();
|
||||
if (this.data.employee_list.length !== 0 && ! this.data.employee_id) {
|
||||
isBlocked = true;
|
||||
}
|
||||
return isBlocked;
|
||||
},
|
||||
|
||||
// Private
|
||||
|
||||
async _checkPin(employeeId, pin, sessionSave = true) {
|
||||
const pinValid = await this.orm.call('hr.employee', 'login', [employeeId, pin, sessionSave]);
|
||||
if (!pinValid) {
|
||||
this.popupEmployeePin(employeeId);
|
||||
return;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
_onBarcodeScanned(barcode) {
|
||||
const employee = this.data.employee_list.find(e => e.barcode === barcode);
|
||||
if (employee) {
|
||||
this.startEmployee(employee.id);
|
||||
} else {
|
||||
return this._super(barcode);
|
||||
}
|
||||
},
|
||||
|
||||
async _onWillStart() {
|
||||
const superMethod = this._super;
|
||||
const employeeId = this.props.action.context.employee_id;
|
||||
if (employeeId) {
|
||||
await this.startEmployee(employeeId);
|
||||
}
|
||||
await superMethod();
|
||||
if (employeeId) {
|
||||
await this.getState();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Tablet.components.SelectionPopup = SelectionPopup;
|
||||
Tablet.components.PinPopup = PinPopup;
|
||||
Tablet.components.WorkingEmployeePopup = WorkingEmployeePopup;
|
||||
22
mrp_workorder_hr/static/src/components/tablet.xml
Normal file
22
mrp_workorder_hr/static/src/components/tablet.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
<t t-inherit="mrp_workorder.Tablet" t-inherit-mode="extension">
|
||||
<xpath expr="//div[hasclass('o_tablet_client_action')]" position="inside">
|
||||
<div t-if="popup['SelectionPopup'].isShown" class="o_tablet_popups o_unblock">
|
||||
<SelectionPopup popupData="popup['SelectionPopup'].data" onClosePopup.bind="closePopup" onSelectEmployee.bind="startEmployee"/>
|
||||
</div>
|
||||
<div t-if="popup['PinPopup'].isShown" class="o_tablet_popups o_unblock">
|
||||
<PinPopup popupData="popup['PinPopup'].data" onClosePopup.bind="closePopup" onPinValidate.bind="redirectToAction"/>
|
||||
</div>
|
||||
<div t-if="popup['WorkingEmployeePopup'].isShown" class="o_tablet_popups o_unblock">
|
||||
<WorkingEmployeePopup
|
||||
popupData="popup['WorkingEmployeePopup'].data"
|
||||
onClosePopup.bind="closePopup"
|
||||
onAddEmployee.bind="popupAddEmployee"
|
||||
onLockEmployee.bind="lockEmployee"
|
||||
onStopEmployee.bind="stopEmployee"
|
||||
onStartEmployee.bind="startEmployee"/>
|
||||
</div>
|
||||
</xpath>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -0,0 +1,80 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { MrpTimer } from "@mrp/widgets/timer";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import time from 'web.time';
|
||||
|
||||
const { Component, onWillStart } = owl;
|
||||
|
||||
export class WorkingEmployeePopup extends Component {
|
||||
setup() {
|
||||
super.setup();
|
||||
this.orm = useService('orm');
|
||||
this.workorderId = this.props.popupData.workorderId;
|
||||
|
||||
onWillStart(() => this._getState())
|
||||
}
|
||||
|
||||
addEmployee() {
|
||||
this.props.onAddEmployee();
|
||||
this.close();
|
||||
}
|
||||
|
||||
lockEmployee(employeeId) {
|
||||
this.startEmployee(employeeId);
|
||||
this.props.onLockEmployee(employeeId);
|
||||
this.close();
|
||||
}
|
||||
|
||||
async stopEmployee(employeeId) {
|
||||
this.props.onStopEmployee(employeeId);
|
||||
this.lines.map(l => {
|
||||
if (l.employee_id === employeeId) {
|
||||
l.ongoing = false;
|
||||
}
|
||||
});
|
||||
this.render();
|
||||
}
|
||||
|
||||
startEmployee(employeeId) {
|
||||
this.props.onStartEmployee(employeeId);
|
||||
this.lines.map(l => {
|
||||
if (l.employee_id === employeeId) {
|
||||
l.ongoing = true;
|
||||
}
|
||||
});
|
||||
this.render();
|
||||
}
|
||||
|
||||
close() {
|
||||
this.props.onClosePopup('WorkingEmployeePopup', true);
|
||||
}
|
||||
|
||||
async _getState() {
|
||||
const productivityLines = await this.orm.call('mrp.workcenter.productivity', 'read_group', [
|
||||
[
|
||||
['workorder_id', '=', this.workorderId],
|
||||
['employee_id', '!=', false],
|
||||
],
|
||||
['duration', 'date_start:array_agg', 'date_end:array_agg'],
|
||||
['employee_id']
|
||||
]);
|
||||
this.lines = productivityLines.map((pl) => {
|
||||
let duration = pl.duration * 60;
|
||||
const ongoingTimerIndex = pl.date_end.indexOf(null);
|
||||
if ( ongoingTimerIndex != -1 ){
|
||||
const additionalDuration = moment(new Date()).diff(moment(time.auto_str_to_date(pl.date_start[ongoingTimerIndex])), 'seconds');
|
||||
duration += additionalDuration;
|
||||
}
|
||||
return {
|
||||
'employee_id': pl.employee_id[0],
|
||||
'employee_name': pl.employee_id[1],
|
||||
'duration': duration,
|
||||
'ongoing': pl.date_end.some(d => !d),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
WorkingEmployeePopup.components = { MrpTimer };
|
||||
WorkingEmployeePopup.template = 'mrp_workorder_hr.WorkingEmployeePopup';
|
||||
@@ -0,0 +1,13 @@
|
||||
.o_popup_employee_selection {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
line-height: 50px;
|
||||
color: black;
|
||||
div {
|
||||
flex-basis: 25%;
|
||||
}
|
||||
.o_popup_employee_name {
|
||||
flex-basis: 50%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
<t t-name="mrp_workorder_hr.WorkingEmployeePopup" owl="1">
|
||||
<div role="dialog">
|
||||
<div class="popup centered">
|
||||
<t t-foreach="lines" t-as="line" t-key="line.employee_id">
|
||||
<div class="o_popup_employee_selection">
|
||||
<div class="o_popup_employee_name" t-on-click="() => this.lockEmployee(line.employee_id)">
|
||||
<button class="btn_employee">
|
||||
<img t-attf-src="/web/image/hr.employee/{{line.employee_id}}/avatar_128" alt="Employee" style="height:35px;" class="ml8 rounded-circle"/>
|
||||
<span class="ml8">
|
||||
<t t-esc="line.employee_name"/>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<MrpTimer ongoing="line.ongoing" duration="line.duration"/>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
|
||||
<button t-if="line.ongoing" class="btn btn-danger text-uppercase" t-on-click="() => this.stopEmployee(line.employee_id)" style="line-height:20px;">Stop</button>
|
||||
<button t-if="!line.ongoing" class="btn btn-warning text-uppercase" t-on-click="() => this.startEmployee(line.employee_id)" style="line-height:20px;">Continue</button>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<footer class="footer">
|
||||
<button class="btn btn-link " t-on-click="addEmployee">+ New Operator</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -0,0 +1,152 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { SelectionPopup } from '@mrp_workorder_hr/components/popup';
|
||||
import { PinPopup } from '@mrp_workorder_hr/components/pin_popup';
|
||||
import { useBus, useService } from "@web/core/utils/hooks";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import {MrpWorkorderKanbanController} from '@mrp_workorder/views/kanban/mrp_workorder_kanban_controller';
|
||||
|
||||
const {onWillStart, useState, onMounted} = owl;
|
||||
|
||||
MrpWorkorderKanbanController.components.SelectionPopup = SelectionPopup;
|
||||
MrpWorkorderKanbanController.components.PinPopup = PinPopup;
|
||||
|
||||
patch(MrpWorkorderKanbanController.prototype, 'mrp_workorder_hr', {
|
||||
setup() {
|
||||
this._super();
|
||||
this.popup = useState({
|
||||
PinPopup: {
|
||||
isShown: false,
|
||||
data: {},
|
||||
},
|
||||
SelectionPopup: {
|
||||
isShown: false,
|
||||
data: {},
|
||||
}
|
||||
});
|
||||
this.notification = useService('notification');
|
||||
this.barcode = useService("barcode");
|
||||
useBus(this.barcode.bus, 'barcode_scanned', (event) => this._onBarcodeScanned(event.detail.barcode));
|
||||
this.workcenterId = this.props.context.default_workcenter_id;
|
||||
this.workcenter = false;
|
||||
this.employee = useState({
|
||||
name: false || this.props.context.employee_name,
|
||||
id: 0 || this.props.context.employee_id,
|
||||
});
|
||||
onWillStart(async () => {
|
||||
await this.onWillStart();
|
||||
});
|
||||
onMounted(() => {
|
||||
this.onMount();
|
||||
});
|
||||
},
|
||||
|
||||
async onWillStart() {
|
||||
if (!this.workcenterId) {
|
||||
return;
|
||||
}
|
||||
const workcenter = await this.orm.read(
|
||||
"mrp.workcenter", [this.workcenterId], ['allow_employee', 'employee_ids']
|
||||
);
|
||||
this.workcenter = workcenter[0];
|
||||
if (!this.workcenter.allow_employee) {
|
||||
return;
|
||||
}
|
||||
const fieldsToRead = ['id', 'name', 'barcode'];
|
||||
const employees_domain = [];
|
||||
if (this.workcenter.employee_ids.length) {
|
||||
employees_domain.push(['id', 'in', this.workcenter.employee_ids]);
|
||||
}
|
||||
this.employees = await this.orm.searchRead(
|
||||
"hr.employee", employees_domain, fieldsToRead,
|
||||
);
|
||||
},
|
||||
|
||||
onMount() {
|
||||
if (this.employeeId) {
|
||||
this.selectEmployee(this.employeeId);
|
||||
}
|
||||
},
|
||||
|
||||
// destroy: function () {
|
||||
// core.bus.off('barcode_scanned', this, this._onBarcodeScanned);
|
||||
// this._super();
|
||||
// },
|
||||
|
||||
openEmployeeSelection() {
|
||||
const employeeList = this.employees.map(employee => Object.create({
|
||||
id: employee.id,
|
||||
item: employee,
|
||||
label: employee.name,
|
||||
isSelected: employee === this.employee.id,
|
||||
}));
|
||||
this.popup.SelectionPopup = {
|
||||
data: { title: this.env._t('Select Employee'), list: employeeList },
|
||||
isShown: true,
|
||||
};
|
||||
},
|
||||
|
||||
async selectEmployee(employeeId, pin) {
|
||||
const employee = this.employees.find(e => e.id === employeeId);
|
||||
const employee_function = this.employee.name && this.employee.id === employeeId ? 'logout' : 'login';
|
||||
const pinValid = await this.orm.call(
|
||||
"hr.employee", employee_function, [employeeId, pin],
|
||||
);
|
||||
if (!pinValid && this.popup.PinPopup.isShown) {
|
||||
this.notification.add(this.env._t('Wrong password !'), {type: 'danger'});
|
||||
return;
|
||||
}
|
||||
if (!pinValid) {
|
||||
this._askPin(employee);
|
||||
return;
|
||||
}
|
||||
|
||||
if (employee_function === 'login') {
|
||||
this.notification.add(this.env._t('Logged in!'), {type: 'success'});
|
||||
this.employee = {
|
||||
name: employee.name,
|
||||
id: employee.id,
|
||||
};
|
||||
if (this.context.openRecord) {
|
||||
this.openRecord(...this.context.openRecord);
|
||||
|
||||
}
|
||||
} else {
|
||||
this.employee = {
|
||||
name: false,
|
||||
id: 0,
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
closePopup(popupName) {
|
||||
this.popup[popupName].isShown = false;
|
||||
},
|
||||
|
||||
_askPin(employee) {
|
||||
this.popup.PinPopup = {
|
||||
data: {employee: employee},
|
||||
isShown: true,
|
||||
};
|
||||
},
|
||||
|
||||
_onBarcodeScanned(barcode) {
|
||||
const employee = this.employees.find(e => e.barcode === barcode);
|
||||
if (employee) {
|
||||
this.selectEmployee(employee.id);
|
||||
} else {
|
||||
this.notification.add(this.env._t('This employee is not allowed on this workcenter'), {type: 'danger'});
|
||||
}
|
||||
},
|
||||
|
||||
async openRecord(record, mode) {
|
||||
if (this.employees && !this.employee.name) {
|
||||
this.context.openRecord = [record, mode];
|
||||
this.openEmployeeSelection();
|
||||
return;
|
||||
}
|
||||
delete this.context.openRecord;
|
||||
Object.assign(this.context, {employee_id: this.employee.id});
|
||||
this._super(...arguments);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,152 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { SelectionPopup } from "@mrp_workorder_hr/components/popup";
|
||||
import { PinPopup } from "@mrp_workorder_hr/components/pin_popup";
|
||||
import core from "web.core";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import {MrpWorkorderListController} from "@mrp_workorder/views/list/mrp_workorder_list_controller";
|
||||
|
||||
const {onWillStart, useState, onMounted} = owl;
|
||||
|
||||
MrpWorkorderListController.components.SelectionPopup = SelectionPopup;
|
||||
MrpWorkorderListController.components.PinPopup = PinPopup;
|
||||
|
||||
patch(MrpWorkorderListController.prototype, "mrp_workorder_hr", {
|
||||
setup() {
|
||||
this._super();
|
||||
this.popup = useState({
|
||||
PinPopup: {
|
||||
isShown: false,
|
||||
data: {},
|
||||
},
|
||||
SelectionPopup: {
|
||||
isShown: false,
|
||||
data: {},
|
||||
}
|
||||
});
|
||||
this.notification = useService("notification");
|
||||
this.orm = useService("orm");
|
||||
this.workcenterId = this.props.context.default_workcenter_id;
|
||||
this.workcenter = false;
|
||||
this.employee = useState({
|
||||
name: false || this.props.context.employee_name,
|
||||
id: 0 || this.props.context.employee_id,
|
||||
});
|
||||
onWillStart(async () => {
|
||||
await this.onWillStart();
|
||||
});
|
||||
onMounted(() => {
|
||||
this.onMount();
|
||||
});
|
||||
},
|
||||
|
||||
async onWillStart() {
|
||||
if (!this.workcenterId) {
|
||||
return;
|
||||
}
|
||||
const workcenter = await this.orm.read(
|
||||
"mrp.workcenter", [this.workcenterId], ["allow_employee", "employee_ids"]
|
||||
);
|
||||
this.workcenter = workcenter[0];
|
||||
if (!this.workcenter.allow_employee) {
|
||||
return;
|
||||
}
|
||||
const fieldsToRead = ["id", "name", "barcode"];
|
||||
const employees_domain = [];
|
||||
if (this.workcenter.employee_ids.length) {
|
||||
employees_domain.push(["id", "in", this.workcenter.employee_ids]);
|
||||
}
|
||||
this.employees = await this.orm.searchRead(
|
||||
"hr.employee", employees_domain, fieldsToRead,
|
||||
);
|
||||
},
|
||||
|
||||
onMount() {
|
||||
if (this.employeeId) {
|
||||
this.selectEmployee(this.employeeId);
|
||||
}
|
||||
core.bus.on("barcode_scanned", this, this._onBarcodeScanned);
|
||||
},
|
||||
|
||||
// destroy: function () {
|
||||
// core.bus.off("barcode_scanned", this, this._onBarcodeScanned);
|
||||
// this._super();
|
||||
// },
|
||||
|
||||
openEmployeeSelection() {
|
||||
const employeeList = this.employees.map(employee => Object.create({
|
||||
id: employee.id,
|
||||
item: employee,
|
||||
label: employee.name,
|
||||
isSelected: employee === this.employee.id,
|
||||
}));
|
||||
this.popup.SelectionPopup = {
|
||||
data: { title: this.env._t("Select Employee"), list: employeeList },
|
||||
isShown: true,
|
||||
};
|
||||
},
|
||||
|
||||
async selectEmployee(employeeId, pin) {
|
||||
const employee = this.employees.find(e => e.id === employeeId);
|
||||
const employee_function = this.employee.name && this.employee.id === employeeId ? "logout" : "login";
|
||||
const pinValid = await this.orm.call(
|
||||
"hr.employee", employee_function, [employeeId, pin],
|
||||
);
|
||||
if (!pinValid && this.popup.PinPopup.isShown) {
|
||||
this.notification.add(this.env._t("Wrong password !"), {type: "danger"});
|
||||
return;
|
||||
}
|
||||
if (!pinValid) {
|
||||
this._askPin(employee);
|
||||
return;
|
||||
}
|
||||
|
||||
if (employee_function === "login") {
|
||||
this.notification.add(this.env._t("Logged in!"), {type: "success"});
|
||||
this.employee = {
|
||||
name: employee.name,
|
||||
id: employee.id,
|
||||
};
|
||||
if (this.context.openRecord) {
|
||||
this.openRecord(...this.context.openRecord);
|
||||
}
|
||||
} else {
|
||||
this.employee = {
|
||||
name: false,
|
||||
id: 0,
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
closePopup(popupName) {
|
||||
this.popup[popupName].isShown = false;
|
||||
},
|
||||
|
||||
_askPin(employee) {
|
||||
this.popup.PinPopup = {
|
||||
data: {employee: employee},
|
||||
isShown: true,
|
||||
};
|
||||
},
|
||||
|
||||
_onBarcodeScanned: function (barcode) {
|
||||
const employee = this.employees.find(e => e.barcode === barcode);
|
||||
if (employee) {
|
||||
this.selectEmployee(employee.id);
|
||||
} else {
|
||||
this.notification.add(this.env._t("This employee is not allowed on this workcenter"), {type: "danger"});
|
||||
}
|
||||
},
|
||||
|
||||
async openRecord(record, mode) {
|
||||
if (this.employees && !this.employee.name) {
|
||||
this.context.openRecord = [record, mode];
|
||||
this.openEmployeeSelection();
|
||||
return;
|
||||
}
|
||||
delete this.context.openRecord;
|
||||
Object.assign(this.context, {employee_id: this.employee.id});
|
||||
this._super(...arguments);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
<t t-inherit="mrp_workorder.overviewButtonsKanban" t-inherit-mode="extension">
|
||||
<xpath expr="//button[hasclass('o_back_button')]" position="after">
|
||||
<button t-if="workcenter.allow_employee" t-on-click="openEmployeeSelection" class="btn btn-secondary o_lock_employee">
|
||||
<span>Log In</span>
|
||||
<t t-if="employee.name">
|
||||
<span t-esc="employee.name" class="ml16 text-dark"></span>
|
||||
</t>
|
||||
</button>
|
||||
</xpath>
|
||||
<xpath expr="//div[last()]" position="after">
|
||||
<div t-if="popup['SelectionPopup'].isShown" class="o_tablet_popups">
|
||||
<SelectionPopup popupData="popup['SelectionPopup'].data" onClosePopup.bind="closePopup" onSelectEmployee.bind="selectEmployee"/>
|
||||
</div>
|
||||
<div t-if="popup['PinPopup'].isShown" class="o_tablet_popups">
|
||||
<PinPopup popupData="popup['PinPopup'].data" onClosePopup.bind="closePopup" onPinValidate.bind="selectEmployee"/>
|
||||
</div>
|
||||
</xpath>
|
||||
</t>
|
||||
</templates>
|
||||
94
mrp_workorder_hr/static/tests/tours/tour_test_workorder.js
Normal file
94
mrp_workorder_hr/static/tests/tours/tour_test_workorder.js
Normal file
@@ -0,0 +1,94 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import tour from 'web_tour.tour';
|
||||
import helper from 'mrp_workorder.tourHelper';
|
||||
|
||||
tour.register('test_production_with_employee', {test: true}, [
|
||||
{trigger: 'div.popup'},
|
||||
{trigger: 'h3:contains("Change Worker")'},
|
||||
{trigger: 'div.selection-item:contains("Arthur")'},
|
||||
{trigger: 'div.popup-numpad'},
|
||||
{trigger: '.popup-numpad button:contains("1")'},
|
||||
{trigger: 'span.highlight:contains("•")'},
|
||||
{trigger: '.popup-numpad button:contains("2")'},
|
||||
{trigger: 'span.highlight:contains("••")'},
|
||||
{trigger: '.popup-numpad button:contains("3")'},
|
||||
{trigger: 'span.highlight:contains("•••")'},
|
||||
{trigger: '.popup-numpad button:contains("4")'},
|
||||
{trigger: 'span.highlight:contains("••••")'},
|
||||
{trigger: 'button.confirm'},
|
||||
{
|
||||
trigger: 'span[title="Arthur Fu"]',
|
||||
run: function () {
|
||||
helper.assertCheckLength(3);
|
||||
helper.assertValidatedCheckLength(0);
|
||||
helper.assertQtyToProduce(2, 2);
|
||||
helper.assertCurrentCheck('Instruction 1');
|
||||
}
|
||||
},
|
||||
{trigger: 'div[name=employee_name]'},
|
||||
{trigger: 'button.btn-link:contains("New")'},
|
||||
{trigger: 'h3:contains("Change Worker")'},
|
||||
{trigger: 'div.selection-item:contains("Thomas")'},
|
||||
{trigger: 'div.popup-numpad'},
|
||||
{trigger: '.popup-numpad button:contains("5")'},
|
||||
{trigger: 'span.highlight:contains("•")'},
|
||||
{trigger: '.popup-numpad button:contains("6")'},
|
||||
{trigger: 'span.highlight:contains("••")'},
|
||||
{trigger: '.popup-numpad button:contains("7")'},
|
||||
{trigger: 'span.highlight:contains("•••")'},
|
||||
{trigger: '.popup-numpad button:contains("8")'},
|
||||
{trigger: 'span.highlight:contains("••••")'},
|
||||
{trigger: 'button.confirm'},
|
||||
{
|
||||
trigger: 'span[title="Thomas Nific"]',
|
||||
run: function () {
|
||||
helper.assertCheckLength(3);
|
||||
helper.assertValidatedCheckLength(0);
|
||||
helper.assertQtyToProduce(2, 2);
|
||||
helper.assertCurrentCheck('Instruction 1');
|
||||
}
|
||||
},
|
||||
{trigger: 'div[name=employee_name]'},
|
||||
{trigger: 'button.btn_employee:contains("Thomas")'},
|
||||
{trigger: 'button[name="action_next"]'},
|
||||
{trigger: 'div[name=qty_producing]:contains("2")'}, //field become readonly
|
||||
{
|
||||
trigger: '.o_tablet_step_ok',
|
||||
run: function () {
|
||||
helper.assertCheckLength(3);
|
||||
helper.assertValidatedCheckLength(1);
|
||||
helper.assertQtyToProduce(2, 2);
|
||||
helper.assertCurrentCheck('Instruction 2');
|
||||
}
|
||||
},
|
||||
{trigger: 'button[name="action_next"]'},
|
||||
{
|
||||
trigger: 'p:contains("third")',
|
||||
run: function () {
|
||||
helper.assertCheckLength(3);
|
||||
helper.assertValidatedCheckLength(2);
|
||||
helper.assertQtyToProduce(2, 2);
|
||||
helper.assertCurrentCheck('Instruction 3');
|
||||
}
|
||||
},
|
||||
{trigger: 'button[name=openMenuPopup]'},
|
||||
{trigger: '.o_tablet_popups'},
|
||||
{trigger: '.btn:contains("Update Instruction")'},
|
||||
{trigger: '.modal-title:contains("Update Instruction")'},
|
||||
// {
|
||||
// trigger: "div[name=note] p",
|
||||
// position: 'bottom',
|
||||
// run: 'text my new instruction',
|
||||
// }, {
|
||||
{
|
||||
trigger: "input#comment",
|
||||
position: 'bottom',
|
||||
run: 'text my reason',
|
||||
},
|
||||
{trigger: '.btn-primary[name="process"]'},
|
||||
{trigger: '.o_tablet_client_action'},
|
||||
{trigger: '.btn-primary[name="action_next"]'},
|
||||
{trigger: '.btn[name=do_finish]'},
|
||||
{trigger: '.o_searchview_input'},
|
||||
]);
|
||||
Reference in New Issue
Block a user