合并企业版代码(未测试,先提交到测试分支)

This commit is contained in:
qihao.gong@jikimo.com
2023-04-14 17:42:23 +08:00
parent 7a7b3d7126
commit d28525526a
1300 changed files with 513579 additions and 5426 deletions

View 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';

View 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>

View 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';

View 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>

View 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;

View 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>

View File

@@ -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';

View File

@@ -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%;
}
}

View File

@@ -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>

View File

@@ -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);
},
});

View File

@@ -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);
},
});

View File

@@ -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>

View 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'},
]);