206 lines
7.0 KiB
JavaScript
206 lines
7.0 KiB
JavaScript
odoo.define('web_studio.ApprovalComponent', function (require) {
|
|
'use strict';
|
|
|
|
const Dialog = require('web.OwlDialog');
|
|
const Popover = require("web.Popover");
|
|
const { useService } = require("@web/core/utils/hooks");
|
|
|
|
const { Component, onMounted, onWillUnmount, onWillStart, onWillUpdateProps, useState } = owl;
|
|
|
|
class ApprovalComponent extends Component {
|
|
//--------------------------------------------------------------------------
|
|
// Lifecycle
|
|
//--------------------------------------------------------------------------
|
|
|
|
setup() {
|
|
this.state = useState({
|
|
entries: null,
|
|
rules: null,
|
|
showInfo: false,
|
|
syncing: false,
|
|
init: true,
|
|
});
|
|
|
|
this.rpc = useService("rpc");
|
|
|
|
onMounted( () => {
|
|
this.env.bus.on('refresh-approval', this, this._onRefresh);
|
|
});
|
|
|
|
onWillUnmount( () => {
|
|
this.env.bus.off('refresh-approval', this, this._onRefresh);
|
|
});
|
|
|
|
onWillStart(async () => {
|
|
await this._fetchApprovalData();
|
|
});
|
|
|
|
onWillUpdateProps(async () => {
|
|
await this._fetchApprovalData();
|
|
});
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Public
|
|
//--------------------------------------------------------------------------
|
|
|
|
/**
|
|
* @param {Number} ruleId: id of the rule for which the entry is requested.
|
|
* @returns {Object}
|
|
*/
|
|
getEntry(ruleId) {
|
|
return this.state.entries.find((e) => e.rule_id[0] === ruleId);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Private
|
|
//--------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Delete an approval entry for a given rule server-side.
|
|
* @private
|
|
* @param {Number} ruleId
|
|
*/
|
|
_cancelApproval(ruleId) {
|
|
this._setSyncing(true);
|
|
this.rpc({
|
|
model: 'studio.approval.rule',
|
|
method: 'delete_approval',
|
|
args: [[ruleId]],
|
|
kwargs: {
|
|
res_id: this.props.resId,
|
|
},
|
|
context: this.env.session.user_context,
|
|
}).then(async () => {
|
|
this._notifyChange();
|
|
await this._fetchApprovalData()
|
|
this._setSyncing(false);
|
|
}).guardedCatch(async () => {
|
|
this._notifyChange();
|
|
await this._fetchApprovalData()
|
|
this._setSyncing(false);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
async _fetchApprovalData() {
|
|
const spec = await this.rpc(
|
|
{
|
|
model: 'studio.approval.rule',
|
|
method: 'get_approval_spec',
|
|
args: [this.props.model, this.props.method, this.props.action],
|
|
kwargs: {
|
|
res_id: !this.props.inStudio && this.props.resId,
|
|
},
|
|
context: this.env.session.user_context,
|
|
},
|
|
{ shadow: true }
|
|
);
|
|
// reformat the dates
|
|
spec.entries.forEach((entry) => {
|
|
entry.long_date = moment.utc(entry.write_date).local().format('LLL');
|
|
entry.short_date = moment.utc(entry.write_date).local().format('LL');
|
|
});
|
|
Object.assign(this.state, spec);
|
|
this.state.init = false;
|
|
}
|
|
|
|
/**
|
|
* Notifies other widgets that an approval change has occurred server-side,
|
|
* this is useful if more than one button with the same action is in the view
|
|
* to avoid displaying conflicting approval data.
|
|
* @private
|
|
*/
|
|
_notifyChange() {
|
|
this.env.bus.trigger('refresh-approval', {
|
|
approvalSpec: [this.props.model, this.props.resId, this.props.method, this.props.action],
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Create or update an approval entry for a specified rule server-side.
|
|
* @private
|
|
* @param {Number} ruleId
|
|
* @param {Boolean} approved
|
|
*/
|
|
_setApproval(ruleId, approved) {
|
|
this._setSyncing(true);
|
|
this.rpc({
|
|
model: 'studio.approval.rule',
|
|
method: 'set_approval',
|
|
args: [[ruleId]],
|
|
kwargs: {
|
|
res_id: this.props.resId,
|
|
approved: approved,
|
|
},
|
|
context: this.env.session.user_context,
|
|
}).then(async () => {
|
|
this._notifyChange();
|
|
await this._fetchApprovalData();
|
|
this._setSyncing(false);
|
|
}).guardedCatch(async () => {
|
|
this._notifyChange();
|
|
await this._fetchApprovalData();
|
|
this._setSyncing(false);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Mark the widget as syncing; this is used to disable buttons while
|
|
* an action is being processed server-side.
|
|
* @param {Boolean} value: true to mark the widget as syncing, false otherwise.
|
|
*/
|
|
_setSyncing(value) {
|
|
this.state.syncing = value;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Handlers
|
|
//--------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Handle notification dispatched by the bus; if another action has modifed
|
|
* approval data server-side for an approval spec that matches this widget,
|
|
* it will update itself.
|
|
* @param {Object} ev
|
|
*/
|
|
_onRefresh(ev) {
|
|
if (
|
|
ev.approvalSpec[0] === this.props.model &&
|
|
ev.approvalSpec[1] === this.props.resId &&
|
|
ev.approvalSpec[2] === this.props.method &&
|
|
ev.approvalSpec[3] === this.props.action
|
|
) {
|
|
// someone clicked on this widget's button, which
|
|
// might trigger approvals server-side
|
|
// refresh the widget
|
|
this._fetchApprovalData();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Display or hide more information through a popover (desktop) or
|
|
* dialog (mobile).
|
|
* @param {DOMEvent} ev
|
|
*/
|
|
_toggleInfo() {
|
|
this.state.showInfo = !this.state.showInfo;
|
|
}
|
|
}
|
|
|
|
ApprovalComponent.template = 'Studio.ApprovalComponent.Legacy';
|
|
ApprovalComponent.components = { Dialog, Popover };
|
|
ApprovalComponent.props = {
|
|
action: [Number, Boolean],
|
|
actionName: { type: String, optional: true },
|
|
inStudio: Boolean,
|
|
method: [String, Boolean],
|
|
model: String,
|
|
resId: { type: Number, optional: true },
|
|
};
|
|
|
|
return ApprovalComponent;
|
|
});
|