first push message
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
from . import controllers
|
||||
from . import models
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
'name': "xf_doc_approval",
|
||||
|
||||
'summary': "Short (1 phrase/line) summary of the module's purpose",
|
||||
|
||||
'description': """
|
||||
Long description of module's purpose
|
||||
""",
|
||||
|
||||
'author': "My Company",
|
||||
'license': 'LGPL-3',
|
||||
'website': "https://www.yourcompany.com",
|
||||
|
||||
# Categories can be used to filter modules in modules listing
|
||||
# Check https://github.com/odoo/odoo/blob/15.0/odoo/addons/base/data/ir_module_category_data.xml
|
||||
# for the full list
|
||||
'category': 'request approval',
|
||||
'version': '0.1',
|
||||
|
||||
# any module necessary for this one to work correctly
|
||||
'depends': ['base', 'web', 'mail','account'],
|
||||
'data': [
|
||||
# always loaded
|
||||
# Access
|
||||
'security/security.xml',
|
||||
'security/ir.model.access.csv',
|
||||
# Views
|
||||
'views/menuitems.xml',
|
||||
'views/team.xml',
|
||||
'views/document_package.xml',
|
||||
'views/approver_wizard.xml',
|
||||
# Data
|
||||
'data/mail_templates.xml',
|
||||
'data/mail_message_subtypes.xml',
|
||||
],
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
'application': True,
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
from . import controllers
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,21 @@
|
||||
# from odoo import http
|
||||
|
||||
|
||||
# class XfDocApproval(http.Controller):
|
||||
# @http.route('/xf_doc_approval/xf_doc_approval', auth='public')
|
||||
# def index(self, **kw):
|
||||
# return "Hello, world"
|
||||
|
||||
# @http.route('/xf_doc_approval/xf_doc_approval/objects', auth='public')
|
||||
# def list(self, **kw):
|
||||
# return http.request.render('xf_doc_approval.listing', {
|
||||
# 'root': '/xf_doc_approval/xf_doc_approval',
|
||||
# 'objects': http.request.env['xf_doc_approval.xf_doc_approval'].search([]),
|
||||
# })
|
||||
|
||||
# @http.route('/xf_doc_approval/xf_doc_approval/objects/<model("xf_doc_approval.xf_doc_approval"):obj>', auth='public')
|
||||
# def object(self, obj, **kw):
|
||||
# return http.request.render('xf_doc_approval.object', {
|
||||
# 'object': obj
|
||||
# })
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="mt_document_package_approval" model="mail.message.subtype">
|
||||
<field name="name">Document Package Sent for Approval</field>
|
||||
<field name="res_model">xf.doc.approval.document.package</field>
|
||||
<field name="default" eval="False"/>
|
||||
<field name="description">Document Package sent for approval</field>
|
||||
</record>
|
||||
|
||||
<record id="mt_document_package_approved" model="mail.message.subtype">
|
||||
<field name="name">Document Package Approved</field>
|
||||
<field name="res_model">xf.doc.approval.document.package</field>
|
||||
<field name="default" eval="True"/>
|
||||
<field name="description">Document Package was approved by all approvers</field>
|
||||
</record>
|
||||
|
||||
<record id="mt_document_package_rejected" model="mail.message.subtype">
|
||||
<field name="name">Document Package Rejected</field>
|
||||
<field name="res_model">xf.doc.approval.document.package</field>
|
||||
<field name="default" eval="True"/>
|
||||
<field name="description">Document Package was rejected. Please see notes in the approvers tab.</field>
|
||||
</record>
|
||||
|
||||
<record id="mt_document_package_cancelled" model="mail.message.subtype">
|
||||
<field name="name">Document Package Cancelled</field>
|
||||
<field name="res_model">xf.doc.approval.document.package</field>
|
||||
<field name="default" eval="True"/>
|
||||
<field name="description">Document Package was cancelled.</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<template id="request_to_approve">
|
||||
<p>
|
||||
Dear colleagues,
|
||||
</p>
|
||||
<p>
|
||||
Please be informed that the document package
|
||||
"<t t-esc="object.name"/>"
|
||||
needs approval from:
|
||||
<t t-esc="', '.join(object.get_current_approvers().mapped('display_name'))"/>.
|
||||
</p>
|
||||
<p>
|
||||
<a t-att-href="'/mail/view?model=%s&res_id=%s' % (object._name, object.id)">
|
||||
View
|
||||
<t t-esc="object._description"/>
|
||||
</a>
|
||||
</p>
|
||||
</template>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -0,0 +1,30 @@
|
||||
<odoo>
|
||||
<data>
|
||||
<!--
|
||||
<record id="object0" model="xf_doc_approval.xf_doc_approval">
|
||||
<field name="name">Object 0</field>
|
||||
<field name="value">0</field>
|
||||
</record>
|
||||
|
||||
<record id="object1" model="xf_doc_approval.xf_doc_approval">
|
||||
<field name="name">Object 1</field>
|
||||
<field name="value">10</field>
|
||||
</record>
|
||||
|
||||
<record id="object2" model="xf_doc_approval.xf_doc_approval">
|
||||
<field name="name">Object 2</field>
|
||||
<field name="value">20</field>
|
||||
</record>
|
||||
|
||||
<record id="object3" model="xf_doc_approval.xf_doc_approval">
|
||||
<field name="name">Object 3</field>
|
||||
<field name="value">30</field>
|
||||
</record>
|
||||
|
||||
<record id="object4" model="xf_doc_approval.xf_doc_approval">
|
||||
<field name="name">Object 4</field>
|
||||
<field name="value">40</field>
|
||||
</record>
|
||||
-->
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -0,0 +1,2 @@
|
||||
from . import document
|
||||
from . import team
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,313 @@
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError, ValidationError, AccessError
|
||||
from .selection import ApprovalMethods, DocumentState, ApproverState, ApprovalStep, DocumentVisibility
|
||||
|
||||
|
||||
class DocApprovalDocumentPackage(models.Model):
|
||||
_name = 'xf.doc.approval.document.package'
|
||||
_inherit = ['mail.thread']
|
||||
_description = 'Document Package'
|
||||
|
||||
active = fields.Boolean(default=True)
|
||||
name = fields.Char(
|
||||
string='Name',
|
||||
required=True,
|
||||
translate=True,
|
||||
readonly=True,
|
||||
tracking=True,
|
||||
)
|
||||
description = fields.Text(
|
||||
string='Description',
|
||||
translate=True,
|
||||
)
|
||||
state = fields.Selection(
|
||||
string='Status',
|
||||
selection=DocumentState.list,
|
||||
required=True,
|
||||
default=DocumentState.default,
|
||||
readonly=True,
|
||||
tracking=True,
|
||||
)
|
||||
approval_state = fields.Selection(
|
||||
string='Approval Status',
|
||||
selection=ApproverState.list,
|
||||
compute='_compute_approval_state',
|
||||
)
|
||||
approval_step = fields.Selection(
|
||||
string='Approval Step',
|
||||
selection=ApprovalStep.list,
|
||||
compute='_compute_approval_step',
|
||||
)
|
||||
method = fields.Selection(
|
||||
string='Approval Method',
|
||||
selection=ApprovalMethods.list,
|
||||
required=True,
|
||||
default=ApprovalMethods.default,
|
||||
readonly=True,
|
||||
)
|
||||
visibility = fields.Selection(
|
||||
string='Visibility',
|
||||
selection=DocumentVisibility.list,
|
||||
required=True,
|
||||
default=DocumentVisibility.default,
|
||||
)
|
||||
initiator_user_id = fields.Many2one(
|
||||
string='Initiator',
|
||||
comodel_name='res.users',
|
||||
required=True,
|
||||
default=lambda self: self.env.user,
|
||||
readonly=True,
|
||||
)
|
||||
company_id = fields.Many2one(
|
||||
string='Company',
|
||||
comodel_name='res.company',
|
||||
required=True,
|
||||
default=lambda self: self.env.company,
|
||||
readonly=True,
|
||||
)
|
||||
approval_team_id = fields.Many2one(
|
||||
string='Approval Team',
|
||||
comodel_name='xf.doc.approval.team',
|
||||
readonly=True,
|
||||
domain="[('company_id', '=', company_id)]",
|
||||
)
|
||||
approver_ids = fields.One2many(
|
||||
string='Approvers',
|
||||
comodel_name='xf.doc.approval.document.approver',
|
||||
inverse_name='document_package_id',
|
||||
readonly=True,
|
||||
)
|
||||
document_ids = fields.One2many(
|
||||
string='Documents',
|
||||
comodel_name='xf.doc.approval.document',
|
||||
inverse_name='document_package_id',
|
||||
readonly=True,
|
||||
)
|
||||
|
||||
is_initiator = fields.Boolean('Is Initiator', compute='_compute_access')
|
||||
is_approver = fields.Boolean('Is Approver', compute='_compute_access')
|
||||
reject_reason = fields.Text('Reject Reason')
|
||||
|
||||
# Compute fields
|
||||
|
||||
@api.depends('state', 'approval_team_id')
|
||||
def _compute_access(self):
|
||||
for record in self:
|
||||
# Check if the current user is initiator (true for admin)
|
||||
record.is_initiator = self.env.user == record.initiator_user_id or self.env.user._is_admin()
|
||||
|
||||
# Check if the document needs approval from current user (true for admin)
|
||||
current_approvers = record.get_current_approvers()
|
||||
responsible = self.env.user in current_approvers.mapped('user_id') or self.env.user._is_admin()
|
||||
record.is_approver = record.approval_state == 'pending' and responsible
|
||||
|
||||
@api.depends('approver_ids.state')
|
||||
def _compute_approval_state(self):
|
||||
for record in self:
|
||||
approvers = record.approver_ids
|
||||
if len(approvers) == len(approvers.filtered(lambda a: a.state == 'approved')):
|
||||
record.approval_state = 'approved'
|
||||
elif approvers.filtered(lambda a: a.state == 'rejected'):
|
||||
record.approval_state = 'rejected'
|
||||
elif approvers.filtered(lambda a: a.state == 'pending'):
|
||||
record.approval_state = 'pending'
|
||||
else:
|
||||
record.approval_state = 'to approve'
|
||||
|
||||
@api.depends('approver_ids.state', 'approver_ids.step')
|
||||
def _compute_approval_step(self):
|
||||
for record in self:
|
||||
approval_step = None
|
||||
steps = record.approver_ids.mapped('step')
|
||||
steps.sort()
|
||||
for step in steps:
|
||||
if record.approver_ids.filtered(lambda a: a.step == step and a.state != 'approved'):
|
||||
approval_step = step
|
||||
break
|
||||
record.approval_step = approval_step
|
||||
|
||||
# Onchange handlers
|
||||
|
||||
@api.onchange('approval_team_id')
|
||||
def onchange_approval_team(self):
|
||||
if self.approval_team_id:
|
||||
|
||||
team_approvers = []
|
||||
for team_approver in self.approval_team_id.approver_ids:
|
||||
team_approvers += [{
|
||||
'step': team_approver.step,
|
||||
'user_id': team_approver.user_id.id,
|
||||
'role': team_approver.role,
|
||||
}]
|
||||
approvers = self.approver_ids.browse([])
|
||||
for a in team_approvers:
|
||||
approvers += approvers.new(a)
|
||||
self.approver_ids = approvers
|
||||
|
||||
@api.onchange('approver_ids')
|
||||
def onchange_approvers(self):
|
||||
if self.approval_team_id:
|
||||
if self.approval_team_id.approver_ids.mapped('user_id') != self.approver_ids.mapped('user_id'):
|
||||
self.approval_team_id = None
|
||||
|
||||
# Validation
|
||||
|
||||
@api.constrains('company_id')
|
||||
def _validate_company(self):
|
||||
for record in self:
|
||||
record.approver_ids.validate_company(record.company_id)
|
||||
|
||||
@api.constrains('state', 'approver_ids')
|
||||
def _check_approvers(self):
|
||||
for record in self:
|
||||
if record.state == 'approval' and not record.approver_ids:
|
||||
raise ValidationError(_('Please add at least one approver!'))
|
||||
|
||||
@api.constrains('state', 'document_ids')
|
||||
def _check_documents(self):
|
||||
for record in self:
|
||||
if record.state == 'approval' and not record.document_ids:
|
||||
raise ValidationError(_('Please add at least one document!'))
|
||||
|
||||
# Helpers
|
||||
def set_state(self, state, vals=None):
|
||||
if vals is None:
|
||||
vals = {}
|
||||
vals.update({'state': state})
|
||||
return self.write(vals)
|
||||
|
||||
def get_next_approvers(self):
|
||||
self.ensure_one()
|
||||
next_approvers = self.approver_ids.filtered(lambda a: a.state == 'to approve').sorted('step')
|
||||
if not next_approvers:
|
||||
return next_approvers
|
||||
next_step = next_approvers[0].step
|
||||
return next_approvers.filtered(lambda a: a.step == next_step)
|
||||
|
||||
def get_current_approvers(self):
|
||||
self.ensure_one()
|
||||
return self.approver_ids.filtered(lambda a: a.state == 'pending' and a.step == self.approval_step)
|
||||
|
||||
def get_current_approver(self):
|
||||
self.ensure_one()
|
||||
current_approvers = self.get_current_approvers()
|
||||
if not current_approvers:
|
||||
raise UserError(_('There are not approvers for this document package!'))
|
||||
|
||||
current_approver = current_approvers.filtered(lambda a: a.user_id == self.env.user)
|
||||
if not current_approver and self.env.user._is_admin():
|
||||
current_approver = current_approvers[0]
|
||||
if not current_approver:
|
||||
raise AccessError(_('You are not allowed to approve this document package!'))
|
||||
return current_approver
|
||||
|
||||
def send_notification(self, view_ref, partner_ids):
|
||||
for record in self:
|
||||
record.message_post_with_source(
|
||||
view_ref,
|
||||
subject=_('Document Approval: %s') % record.name,
|
||||
partner_ids=partner_ids,
|
||||
subtype_xmlid='mail.mt_note',
|
||||
)
|
||||
|
||||
# User actions
|
||||
|
||||
def action_send_for_approval(self):
|
||||
for record in self:
|
||||
if record.state == 'draft' and record.approver_ids:
|
||||
# Subscribe approvers
|
||||
record.message_subscribe(partner_ids=record.approver_ids.mapped('user_id').mapped('partner_id').ids)
|
||||
if record.approval_state == 'pending':
|
||||
raise UserError(_('The document package have already been sent for approval!'))
|
||||
elif record.approval_state == 'approved':
|
||||
raise UserError(_('The document package have already been approved!'))
|
||||
elif record.approval_state == 'rejected':
|
||||
raise UserError(_('The document package was rejected! To send it for approval again, please update document(s) first.'))
|
||||
elif record.approval_state == 'to approve':
|
||||
next_approvers = record.get_next_approvers()
|
||||
if next_approvers:
|
||||
if record.state == 'draft':
|
||||
record.state = 'approval'
|
||||
next_approvers.write({'state': 'pending'})
|
||||
partner_ids = next_approvers.mapped('user_id').mapped('partner_id').ids
|
||||
record.send_notification('xf_doc_approval.request_to_approve', partner_ids)
|
||||
else:
|
||||
raise UserError(_('There are not approvers for this document package!'))
|
||||
|
||||
def action_approve_wizard(self):
|
||||
self.ensure_one()
|
||||
current_approver = self.get_current_approver()
|
||||
return current_approver.action_wizard('action_approve_wizard', _('Approve'))
|
||||
|
||||
def action_reject_wizard(self):
|
||||
self.ensure_one()
|
||||
current_approver = self.get_current_approver()
|
||||
return current_approver.action_wizard('action_reject_wizard', _('Reject'))
|
||||
|
||||
def action_draft(self):
|
||||
for record in self:
|
||||
record.approver_ids.write({'state': 'to approve', 'notes': None})
|
||||
record.write({'state': 'draft', 'reject_reason': None})
|
||||
return True
|
||||
|
||||
def action_cancel(self):
|
||||
if not self.env.user._is_admin() and self.filtered(lambda record: record.state == 'approved'):
|
||||
raise UserError(_("Cannot cancel a document package that is approved."))
|
||||
return self.set_state('cancelled')
|
||||
|
||||
def action_finish_approval(self):
|
||||
for record in self:
|
||||
if record.approval_state == 'approved':
|
||||
record.state = 'approved'
|
||||
else:
|
||||
raise UserError(_('Document Package must be fully approved!'))
|
||||
|
||||
# Built-in methods
|
||||
|
||||
def unlink(self):
|
||||
if any(self.filtered(lambda record: record.state not in ('draft', 'cancelled'))):
|
||||
raise UserError(_('You cannot delete a record which is not draft or cancelled!'))
|
||||
return super(DocApprovalDocumentPackage, self).unlink()
|
||||
|
||||
def _track_subtype(self, init_values):
|
||||
self.ensure_one()
|
||||
if 'state' in init_values and self.state == 'approval':
|
||||
return self.env.ref('xf_doc_approval.mt_document_package_approval')
|
||||
elif 'state' in init_values and self.state == 'approved':
|
||||
return self.env.ref('xf_doc_approval.mt_document_package_approved')
|
||||
elif 'state' in init_values and self.state == 'cancelled':
|
||||
return self.env.ref('xf_doc_approval.mt_document_package_cancelled')
|
||||
elif 'state' in init_values and self.state == 'rejected':
|
||||
return self.env.ref('xf_doc_approval.mt_document_package_rejected')
|
||||
|
||||
return super(DocApprovalDocumentPackage, self)._track_subtype(init_values)
|
||||
|
||||
|
||||
class DocApprovalDocument(models.Model):
|
||||
_name = 'xf.doc.approval.document'
|
||||
_description = 'Document'
|
||||
|
||||
document_package_id = fields.Many2one(
|
||||
string='Document Package',
|
||||
comodel_name='xf.doc.approval.document.package',
|
||||
required=True,
|
||||
ondelete='cascade',
|
||||
)
|
||||
name = fields.Char(
|
||||
string='Name',
|
||||
required=True,
|
||||
translate=True,
|
||||
)
|
||||
file = fields.Binary(
|
||||
string='File',
|
||||
required=True,
|
||||
attachment=True,
|
||||
)
|
||||
file_name = fields.Char(
|
||||
string='File Name'
|
||||
)
|
||||
|
||||
@api.onchange('file_name')
|
||||
def _onchange_file_name(self):
|
||||
if self.file_name and not self.name:
|
||||
self.name = self.file_name
|
||||
@@ -0,0 +1,57 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
class Selection(object):
|
||||
list = []
|
||||
folded = []
|
||||
default = None
|
||||
|
||||
@classmethod
|
||||
def name(cls, state):
|
||||
states_dict = dict(cls.list)
|
||||
if state in states_dict:
|
||||
return states_dict[state]
|
||||
|
||||
@classmethod
|
||||
def values(cls):
|
||||
return list(dict(cls.list))
|
||||
|
||||
|
||||
class ApproverState(Selection):
|
||||
list = [
|
||||
('to approve', 'To Approve'),
|
||||
('pending', 'Pending'),
|
||||
('approved', 'Approved'),
|
||||
('rejected', 'Rejected'),
|
||||
]
|
||||
default = list[0][0]
|
||||
|
||||
|
||||
class ApprovalMethods(Selection):
|
||||
list = [
|
||||
('button', 'Button'),
|
||||
]
|
||||
default = list[0][0]
|
||||
|
||||
|
||||
class DocumentState(Selection):
|
||||
list = [
|
||||
('draft', 'Draft'),
|
||||
('approval', 'Approval'),
|
||||
('approved', 'Approved'),
|
||||
('cancelled', 'Cancelled'),
|
||||
('rejected', 'Rejected'),
|
||||
]
|
||||
default = list[0][0]
|
||||
|
||||
class DocumentVisibility(Selection):
|
||||
list = [
|
||||
('all_users', 'All Users'),
|
||||
('followers', 'Followers'),
|
||||
('approvers', 'Approvers'),
|
||||
]
|
||||
default = list[0][0]
|
||||
|
||||
class ApprovalStep(Selection):
|
||||
step_range = list(range(1, 21))
|
||||
list = [("{:02d}".format(step), "{:02d}".format(step)) for step in step_range]
|
||||
default = list[0][0]
|
||||
@@ -0,0 +1,195 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import ValidationError
|
||||
from .selection import ApproverState, ApprovalMethods, ApprovalStep
|
||||
|
||||
|
||||
class DocApprovalTeam(models.Model):
|
||||
_name = 'xf.doc.approval.team'
|
||||
_description = 'Doc Approval Team'
|
||||
|
||||
active = fields.Boolean('Active', default=True)
|
||||
|
||||
name = fields.Char(
|
||||
string='Name',
|
||||
required=True,
|
||||
)
|
||||
user_id = fields.Many2one(
|
||||
string='Team Leader',
|
||||
comodel_name='res.users',
|
||||
required=True,
|
||||
default=lambda self: self.env.user,
|
||||
index=True
|
||||
)
|
||||
company_id = fields.Many2one(
|
||||
string='Company',
|
||||
comodel_name='res.company',
|
||||
required=True,
|
||||
default=lambda self: self.env.company.id,
|
||||
index=True,
|
||||
)
|
||||
approver_ids = fields.One2many(
|
||||
string='Approvers',
|
||||
comodel_name='xf.doc.approval.team.approver',
|
||||
inverse_name='team_id',
|
||||
)
|
||||
|
||||
# Validation
|
||||
|
||||
@api.constrains('company_id')
|
||||
def _check_team_company(self):
|
||||
for team in self:
|
||||
team.approver_ids.validate_company(team.company_id)
|
||||
|
||||
|
||||
class DocApprovalApproverAbstract(models.Model):
|
||||
_name = 'xf.doc.approval.approver.abstract'
|
||||
_description = 'Abstract Approver'
|
||||
_order = 'step'
|
||||
_rec_name = 'user_id'
|
||||
step = fields.Selection(
|
||||
string='Step',
|
||||
selection=ApprovalStep.list,
|
||||
required=True,
|
||||
default=ApprovalStep.default,
|
||||
)
|
||||
user_id = fields.Many2one(
|
||||
string='Approver',
|
||||
comodel_name='res.users',
|
||||
required=True
|
||||
)
|
||||
role = fields.Char(
|
||||
string='Role/Position',
|
||||
required=True,
|
||||
default="Approver"
|
||||
)
|
||||
|
||||
# Onchange handlers
|
||||
|
||||
@api.onchange('user_id')
|
||||
def _detect_user_role(self):
|
||||
for approver in self:
|
||||
# if user related to employee, try to get job title for hr.employee
|
||||
employee = hasattr(approver.user_id, 'employee_ids') and getattr(approver.user_id, 'employee_ids')
|
||||
employee_job_id = hasattr(employee, 'job_id') and getattr(employee, 'job_id')
|
||||
employee_job_title = employee_job_id.name if employee_job_id else False
|
||||
if employee_job_title:
|
||||
approver.role = employee_job_title
|
||||
continue
|
||||
# if user related partner, try to get job title for res.partner
|
||||
partner = approver.user_id.partner_id
|
||||
partner_job_title = hasattr(partner, 'function') and getattr(partner, 'function')
|
||||
if partner_job_title:
|
||||
approver.role = partner_job_title
|
||||
|
||||
# Validation
|
||||
|
||||
@api.constrains('user_id')
|
||||
def _check_users(self):
|
||||
for approver in self:
|
||||
if not approver.user_id.has_group('xf_doc_approval.group_xf_doc_approval_user'):
|
||||
raise ValidationError(_('%s does not have access to the Doc Approval module.') % (approver.user_id.name,)
|
||||
+ '\n' +
|
||||
_('Please ask system administrator to add him/her to the Doc Approval module group first.'))
|
||||
|
||||
def validate_company(self, company):
|
||||
if not company:
|
||||
return
|
||||
for approver in self:
|
||||
if company not in approver.user_id.company_ids:
|
||||
raise ValidationError(
|
||||
_('%s does not have access to the company %s') % (approver.user_id.name, company.name))
|
||||
|
||||
|
||||
class DocApprovalTeamApprover(models.Model):
|
||||
_name = 'xf.doc.approval.team.approver'
|
||||
_inherit = ['xf.doc.approval.approver.abstract']
|
||||
_description = 'Approval Team Member'
|
||||
|
||||
team_id = fields.Many2one(
|
||||
string='Team',
|
||||
comodel_name='xf.doc.approval.team',
|
||||
required=True,
|
||||
ondelete='cascade'
|
||||
)
|
||||
|
||||
# Validation
|
||||
|
||||
@api.constrains('user_id', 'team_id')
|
||||
def _check_users(self):
|
||||
for approver in self:
|
||||
approver.validate_company(approver.team_id.company_id)
|
||||
return super(DocApprovalTeamApprover, self)._check_users()
|
||||
|
||||
|
||||
class DocApprovalDocumentApprover(models.Model):
|
||||
_name = 'xf.doc.approval.document.approver'
|
||||
_inherit = ['xf.doc.approval.approver.abstract']
|
||||
_description = 'Doc Approver'
|
||||
|
||||
team_approver_id = fields.Many2one(
|
||||
string='Doc Team Approver',
|
||||
comodel_name='xf.doc.approval.team.approver',
|
||||
ondelete='set null'
|
||||
)
|
||||
document_package_id = fields.Many2one(
|
||||
string='Document Package',
|
||||
comodel_name='xf.doc.approval.document.package',
|
||||
required=True,
|
||||
ondelete='cascade',
|
||||
)
|
||||
method = fields.Selection(
|
||||
string='Method',
|
||||
related='document_package_id.method',
|
||||
readonly=True,
|
||||
)
|
||||
state = fields.Selection(
|
||||
string='Status',
|
||||
selection=ApproverState.list,
|
||||
readonly=True,
|
||||
required=True,
|
||||
default=ApproverState.default
|
||||
)
|
||||
notes = fields.Text(
|
||||
string='Notes',
|
||||
readonly=True,
|
||||
)
|
||||
|
||||
# Validation
|
||||
|
||||
@api.constrains('user_id', 'document_package_id')
|
||||
def _check_users(self):
|
||||
for approver in self:
|
||||
approver.validate_company(approver.document_package_id.company_id)
|
||||
return super(DocApprovalDocumentApprover, self)._check_users()
|
||||
|
||||
# User actions
|
||||
|
||||
def action_wizard(self, view_ref, window_title):
|
||||
self.ensure_one()
|
||||
view = self.env.ref('xf_doc_approval.' + view_ref)
|
||||
return {
|
||||
'name': window_title,
|
||||
'type': 'ir.actions.act_window',
|
||||
'view_type': 'form',
|
||||
'view_mode': 'form',
|
||||
'res_model': self._name,
|
||||
'res_id': self.id,
|
||||
'views': [(view.id, 'form')],
|
||||
'view_id': view.id,
|
||||
'target': 'new'
|
||||
}
|
||||
|
||||
def action_approve(self):
|
||||
for approver in self:
|
||||
document_package = approver.document_package_id
|
||||
approver.state = 'approved'
|
||||
if document_package.approval_state == 'to approve':
|
||||
document_package.sudo().action_send_for_approval()
|
||||
elif document_package.approval_state == 'approved':
|
||||
document_package.sudo().action_finish_approval()
|
||||
|
||||
def action_reject(self):
|
||||
for approver in self:
|
||||
approver.state = 'rejected'
|
||||
approver.document_package_id.sudo().set_state('rejected', {'reject_reason': approver.notes})
|
||||
@@ -0,0 +1,15 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_user_xf_doc_approval_team,access_user_xf_doc_approval_team,model_xf_doc_approval_team,xf_doc_approval.group_xf_doc_approval_user,1,0,0,0
|
||||
access_user_xf_doc_approval_team_approver,access_user_xf_doc_approval_team_approver,model_xf_doc_approval_team_approver,xf_doc_approval.group_xf_doc_approval_user,1,0,0,0
|
||||
access_user_xf_doc_approval_document_approver,access_user_xf_doc_approval_document_approver,model_xf_doc_approval_document_approver,xf_doc_approval.group_xf_doc_approval_user,1,1,0,0
|
||||
access_user_xf_doc_approval_document,access_user_xf_doc_approval_document,model_xf_doc_approval_document,xf_doc_approval.group_xf_doc_approval_user,1,0,0,0
|
||||
access_user_xf_doc_approval_document_package,access_user_xf_doc_approval_document_package,model_xf_doc_approval_document_package,xf_doc_approval.group_xf_doc_approval_user,1,0,0,0
|
||||
access_initiator_xf_doc_approval_document_approver,access_initiator_xf_doc_approval_document_approver,model_xf_doc_approval_document_approver,xf_doc_approval.group_xf_doc_approval_initiator,1,1,1,1
|
||||
access_initiator_xf_doc_approval_document,access_initiator_xf_doc_approval_document,model_xf_doc_approval_document,xf_doc_approval.group_xf_doc_approval_initiator,1,1,1,1
|
||||
access_initiator_xf_doc_approval_document_package,access_initiator_xf_doc_approval_document_package,model_xf_doc_approval_document_package,xf_doc_approval.group_xf_doc_approval_initiator,1,1,1,1
|
||||
access_tl_xf_doc_approval_team,access_tl_xf_doc_approval_team,model_xf_doc_approval_team,xf_doc_approval.group_xf_doc_approval_team_leader,1,1,1,1
|
||||
access_tl_xf_doc_approval_team_approver,access_tl_xf_doc_approval_team_approver,model_xf_doc_approval_team_approver,xf_doc_approval.group_xf_doc_approval_team_leader,1,1,1,1
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -0,0 +1,203 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<record id="module_category_xf_doc_approval" model="res.groups.privilege">
|
||||
<field name="name">Doc Approval</field>
|
||||
<field name="description">Helps you approve single documents and document packages.</field>
|
||||
<field name="sequence">15</field>
|
||||
</record>
|
||||
|
||||
<record id="group_xf_doc_approval_user" model="res.groups">
|
||||
<field name="name">User</field>
|
||||
<field name="privilege_id" ref="module_category_xf_doc_approval"/>
|
||||
</record>
|
||||
|
||||
<record id="group_xf_doc_approval_initiator" model="res.groups">
|
||||
<field name="name">Initiator</field>
|
||||
<field name="privilege_id" ref="module_category_xf_doc_approval"/>
|
||||
<field name="implied_ids" eval="[(4, ref('group_xf_doc_approval_user'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="group_xf_doc_approval_team_leader" model="res.groups">
|
||||
<field name="name">Team Leader</field>
|
||||
<field name="privilege_id" ref="module_category_xf_doc_approval"/>
|
||||
<field name="implied_ids" eval="[(4, ref('group_xf_doc_approval_initiator'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="group_xf_doc_approval_manager" model="res.groups">
|
||||
<field name="name">Manager</field>
|
||||
<field name="privilege_id" ref="module_category_xf_doc_approval"/>
|
||||
<field name="implied_ids" eval="[(4, ref('group_xf_doc_approval_team_leader'))]"/>
|
||||
<field name="user_ids" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
|
||||
</record>
|
||||
|
||||
<!-- Rules -->
|
||||
|
||||
<!-- Company Based Global Rules -->
|
||||
|
||||
<record model="ir.rule" id="xf_doc_approval_team_comp_rule">
|
||||
<field name="name">Approval Team (multi-company)</field>
|
||||
<field name="model_id" ref="model_xf_doc_approval_team"/>
|
||||
<field name="domain_force">['|',('company_id','=',False),('company_id', 'in', company_ids)]</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.rule" id="xf_doc_approval_document_package_comp_rule">
|
||||
<field name="name">Document Package (multi-company)</field>
|
||||
<field name="model_id" ref="model_xf_doc_approval_document_package"/>
|
||||
<field name="domain_force">['|',('company_id','=',False),('company_id', 'in', company_ids)]</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.rule" id="xf_doc_approval_document_comp_rule">
|
||||
<field name="name">Document (multi-company)</field>
|
||||
<field name="model_id" ref="model_xf_doc_approval_document"/>
|
||||
<field name="domain_force">['|',('document_package_id.company_id','=',False),('document_package_id.company_id', 'in', company_ids)]</field>
|
||||
</record>
|
||||
|
||||
<!-- User Rules -->
|
||||
|
||||
<record model="ir.rule" id="model_xf_doc_approval_document_package_user_rule">
|
||||
<field name="name">User Access for Document Package</field>
|
||||
<field name="model_id" ref="model_xf_doc_approval_document_package"/>
|
||||
<field name="domain_force">
|
||||
[
|
||||
'|','|','|',
|
||||
('visibility', '=', 'all_users'),
|
||||
'&', ('visibility', '=', 'followers'), ('message_partner_ids', 'in', [user.partner_id.id]),
|
||||
('approver_ids.user_id', '=', user.id),
|
||||
('initiator_user_id', '=', user.id),
|
||||
]
|
||||
</field>
|
||||
<field name="groups" eval="[(4,ref('xf_doc_approval.group_xf_doc_approval_user'))]"/>
|
||||
<field name="perm_read" eval="1"/>
|
||||
<field name="perm_write" eval="0"/>
|
||||
<field name="perm_create" eval="0"/>
|
||||
<field name="perm_unlink" eval="0"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.rule" id="model_xf_doc_approval_document_user_rule">
|
||||
<field name="name">User Access for Document</field>
|
||||
<field name="model_id" ref="model_xf_doc_approval_document"/>
|
||||
<field name="domain_force">
|
||||
[
|
||||
'|','|','|',
|
||||
('document_package_id.visibility', '=', 'all_users'),
|
||||
'&', ('document_package_id.visibility', '=', 'followers'), ('document_package_id.message_partner_ids', 'in', [user.partner_id.id]),
|
||||
('document_package_id.approver_ids.user_id', '=', user.id),
|
||||
('document_package_id.initiator_user_id', '=', user.id),
|
||||
]
|
||||
</field>
|
||||
<field name="groups" eval="[(4,ref('xf_doc_approval.group_xf_doc_approval_user'))]"/>
|
||||
<field name="perm_read" eval="1"/>
|
||||
<field name="perm_write" eval="0"/>
|
||||
<field name="perm_create" eval="0"/>
|
||||
<field name="perm_unlink" eval="0"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.rule" id="model_xf_doc_approval_document_approver_user_rule">
|
||||
<field name="name">User Access for Document Approver</field>
|
||||
<field name="model_id" ref="model_xf_doc_approval_document_approver"/>
|
||||
<field name="domain_force">
|
||||
[
|
||||
'|','|','|',
|
||||
('document_package_id.visibility', '=', 'all_users'),
|
||||
'&', ('document_package_id.visibility', '=', 'followers'), ('document_package_id.message_partner_ids', 'in', [user.partner_id.id]),
|
||||
('document_package_id.approver_ids.user_id', '=', user.id),
|
||||
('document_package_id.initiator_user_id', '=', user.id),
|
||||
]
|
||||
</field>
|
||||
<field name="groups" eval="[(4,ref('xf_doc_approval.group_xf_doc_approval_user'))]"/>
|
||||
<field name="perm_read" eval="1"/>
|
||||
<field name="perm_write" eval="0"/>
|
||||
<field name="perm_create" eval="0"/>
|
||||
<field name="perm_unlink" eval="0"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.rule" id="model_xf_doc_approval_document_approver_user_rule">
|
||||
<field name="name">User Access for Document Approver</field>
|
||||
<field name="model_id" ref="model_xf_doc_approval_document_approver"/>
|
||||
<field name="domain_force">[('user_id', '=', user.id)]</field>
|
||||
<field name="groups" eval="[(4,ref('xf_doc_approval.group_xf_doc_approval_user'))]"/>
|
||||
<field name="perm_read" eval="0"/>
|
||||
<field name="perm_write" eval="1"/>
|
||||
<field name="perm_create" eval="0"/>
|
||||
<field name="perm_unlink" eval="0"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.rule" id="model_xf_doc_approval_document_package_initiator_rule">
|
||||
<field name="name">Initiator Access for Document Package</field>
|
||||
<field name="model_id" ref="model_xf_doc_approval_document_package"/>
|
||||
<field name="domain_force">[('initiator_user_id', '=', user.id)]</field>
|
||||
<field name="groups" eval="[(4,ref('xf_doc_approval.group_xf_doc_approval_initiator'))]"/>
|
||||
<field name="perm_read" eval="0"/>
|
||||
<field name="perm_write" eval="1"/>
|
||||
<field name="perm_create" eval="1"/>
|
||||
<field name="perm_unlink" eval="1"/>
|
||||
</record>
|
||||
<record model="ir.rule" id="model_xf_doc_approval_document_package_account__user_rule">
|
||||
<field name="name">Initiator Access for Document Package</field>
|
||||
<field name="model_id" ref="model_xf_doc_approval_document_package"/>
|
||||
<field name="domain_force">[]</field>
|
||||
<field name="groups" eval="[(4,ref('account.group_account_user'))]"/>
|
||||
<field name="perm_read" eval="1"/>
|
||||
<field name="perm_write" eval="1"/>
|
||||
<field name="perm_create" eval="0"/>
|
||||
<field name="perm_unlink" eval="0"/>
|
||||
</record>
|
||||
<record model="ir.rule" id="model_xf_doc_approval_document_package_account_manager_rule">
|
||||
<field name="name">Initiator Access for Document Package</field>
|
||||
<field name="model_id" ref="model_xf_doc_approval_document_package"/>
|
||||
<field name="domain_force">[]</field>
|
||||
<field name="groups" eval="[(4,ref('account.group_account_manager'))]"/>
|
||||
<field name="perm_read" eval="1"/>
|
||||
<field name="perm_write" eval="1"/>
|
||||
<field name="perm_create" eval="0"/>
|
||||
<field name="perm_unlink" eval="0"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.rule" id="model_xf_doc_approval_document_initiator_rule">
|
||||
<field name="name">Initiator Access for Document</field>
|
||||
<field name="model_id" ref="model_xf_doc_approval_document"/>
|
||||
<field name="domain_force">[('document_package_id.initiator_user_id', '=', user.id)]</field>
|
||||
<field name="groups" eval="[(4,ref('xf_doc_approval.group_xf_doc_approval_initiator'))]"/>
|
||||
<field name="perm_read" eval="0"/>
|
||||
<field name="perm_write" eval="1"/>
|
||||
<field name="perm_create" eval="1"/>
|
||||
<field name="perm_unlink" eval="1"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.rule" id="model_xf_doc_approval_document_approver_initiator_rule">
|
||||
<field name="name">Initiator Access for Document Approver</field>
|
||||
<field name="model_id" ref="model_xf_doc_approval_document_approver"/>
|
||||
<field name="domain_force">[('document_package_id.initiator_user_id', '=', user.id)]</field>
|
||||
<field name="groups" eval="[(4,ref('xf_doc_approval.group_xf_doc_approval_initiator'))]"/>
|
||||
<field name="perm_read" eval="0"/>
|
||||
<field name="perm_write" eval="1"/>
|
||||
<field name="perm_create" eval="1"/>
|
||||
<field name="perm_unlink" eval="1"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.rule" id="model_xf_doc_approval_team_tl_rule">
|
||||
<field name="name">Team Leader Access for Approval Team</field>
|
||||
<field name="model_id" ref="model_xf_doc_approval_team"/>
|
||||
<field name="domain_force">[('user_id', '=', user.id)]</field>
|
||||
<field name="groups" eval="[(4,ref('xf_doc_approval.group_xf_doc_approval_team_leader'))]"/>
|
||||
<field name="perm_read" eval="0"/>
|
||||
<field name="perm_write" eval="1"/>
|
||||
<field name="perm_create" eval="1"/>
|
||||
<field name="perm_unlink" eval="1"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.rule" id="model_xf_doc_approval_team_approver_tl_rule">
|
||||
<field name="name">Team Leader Access for Approval Team Member</field>
|
||||
<field name="model_id" ref="model_xf_doc_approval_team_approver"/>
|
||||
<field name="domain_force">[('team_id.user_id', '=', user.id)]</field>
|
||||
<field name="groups" eval="[(4,ref('xf_doc_approval.group_xf_doc_approval_team_leader'))]"/>
|
||||
<field name="perm_read" eval="0"/>
|
||||
<field name="perm_write" eval="1"/>
|
||||
<field name="perm_create" eval="1"/>
|
||||
<field name="perm_unlink" eval="1"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="action_approve_wizard" model="ir.ui.view">
|
||||
<field name="name">action_approve_wizard</field>
|
||||
<field name="model">xf.doc.approval.document.approver</field>
|
||||
<field name="priority">20</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<sheet>
|
||||
<field name="method" invisible="True"/>
|
||||
<group name="notes">
|
||||
<field name="notes" readonly="False" placeholder="Some comments"/>
|
||||
</group>
|
||||
</sheet>
|
||||
<footer>
|
||||
<button name="action_approve" string="Approve" type="object" class="btn-primary"
|
||||
default_focus="1"/>
|
||||
<button string="Cancel" class="btn-default" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="action_reject_wizard" model="ir.ui.view">
|
||||
<field name="name">action_reject_wizard</field>
|
||||
<field name="model">xf.doc.approval.document.approver</field>
|
||||
<field name="priority">20</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<sheet>
|
||||
<field name="method" invisible="True"/>
|
||||
<group name="notes">
|
||||
<field name="notes" required="True" readonly="False" placeholder="Reject reason or comments"/>
|
||||
</group>
|
||||
</sheet>
|
||||
<footer>
|
||||
<button name="action_reject" string="Reject" type="object" class="btn-primary"
|
||||
default_focus="1"/>
|
||||
<button string="Cancel" class="btn-default" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -0,0 +1,126 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="xf_doc_approval_document_package_form" model="ir.ui.view">
|
||||
<field name="name">xf_doc_approval_document_package_form</field>
|
||||
<field name="model">xf.doc.approval.document.package</field>
|
||||
<field name="arch" type="xml">
|
||||
<form class="o_sale_order">
|
||||
<!-- Hidden fields for logic -->
|
||||
<field name="is_initiator" invisible="True"/>
|
||||
<field name="is_approver" invisible="True"/>
|
||||
|
||||
<header>
|
||||
<button string="Send for Approval" name="action_send_for_approval" type="object" class="oe_highlight" invisible="state!='draft' or not is_initiator" confirm="Please confirm that you want to send documents for approval"/>
|
||||
<button string="Approve" name="action_approve_wizard" type="object" class="oe_highlight" invisible="state!='approval' or not is_approver"/>
|
||||
<button string="Reject" name="action_reject_wizard" type="object" invisible="state!='approval' or not is_approver"/>
|
||||
<button string="Set to Draft" name="action_draft" type="object" invisible="state not in ['rejected','cancelled'] or not is_initiator"/>
|
||||
<button string="Cancel" name="action_cancel" type="object" invisible="state!='approval' or not is_initiator" confirm="Please confirm that you want to cancel approval process"/>
|
||||
<button string="Force Cancel" name="action_cancel" type="object" invisible="state!='approved'" groups="base.group_system"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,approval,approved"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<label for="name"/>
|
||||
<h1>
|
||||
<field name="name" placeholder="Request Name" nolabel="1" readonly="not state or state != 'draft'"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group name="approvers" string="Approvers">
|
||||
<field name="approver_ids" nolabel="1" colspan="2" readonly="not state or state != 'draft'">
|
||||
<list editable="bottom">
|
||||
<field name="step"/>
|
||||
<field name="user_id"/>
|
||||
<field name="role"/>
|
||||
<field name="state"/>
|
||||
<field name="method" invisible="True"/>
|
||||
<field name="notes"/>
|
||||
</list>
|
||||
<form>
|
||||
<group>
|
||||
<group>
|
||||
<field name="user_id"/>
|
||||
<field name="role"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="step"/>
|
||||
<field name="state"/>
|
||||
<field name="method" invisible="True"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<field name="notes"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</group>
|
||||
<group invisible="not is_initiator">
|
||||
<group name="visibility" string="Visibility">
|
||||
<field name="company_id" readonly="not state or state != 'draft'" widget="selection"/>
|
||||
<field name="visibility"/>
|
||||
</group>
|
||||
<group name="approval" string="Approval">
|
||||
<field name="approval_team_id" readonly="not state or state != 'draft'" widget="selection"/>
|
||||
<field name="method" readonly="not state or state != 'draft'"/>
|
||||
<field name="approval_state"/>
|
||||
<field name="approval_step"/>
|
||||
</group>
|
||||
</group>
|
||||
<group name="documents" string="Documents">
|
||||
<field name="document_ids" nolabel="1" colspan="2" readonly="not state or state != 'draft'">
|
||||
<list editable="bottom">
|
||||
<field name="name"/>
|
||||
<field name="file" widget="binary" filename="file_name"/>
|
||||
<field name="file_name" column_invisible="True"/>
|
||||
</list>
|
||||
<form>
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="file" widget="binary" filename="file_name"/>
|
||||
<field name="file_name" invisible="True"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</group>
|
||||
<group>
|
||||
<field name="initiator_user_id" readonly="not state or state != 'draft'"/>
|
||||
</group>
|
||||
<group name="description" string="Description" invisible="state!='draft' or not description">
|
||||
<field name="description" nolabel="1" colspan="2"/>
|
||||
</group>
|
||||
</sheet>
|
||||
<chatter/>
|
||||
<!-- REMOVED MANUAL CHATTER BLOCK -->
|
||||
<!-- Odoo 19 automatically adds the Chatter if the model inherits mail.thread -->
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="xf_doc_approval_document_package_tree" model="ir.ui.view">
|
||||
<field name="name">xf_doc_approval_document_package_tree</field>
|
||||
<field name="model">xf.doc.approval.document.package</field>
|
||||
<field name="arch" type="xml">
|
||||
<list>
|
||||
<field name="name"/>
|
||||
<field name="initiator_user_id"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
<field name="state"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="action_xf_doc_approval_document_package" model="ir.actions.act_window">
|
||||
<field name="name">Documents</field>
|
||||
<field name="res_model">xf.doc.approval.document.package</field>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
id="menu_xf_doc_approval_document_package"
|
||||
action="action_xf_doc_approval_document_package"
|
||||
parent="menu_xf_doc_approval_root"
|
||||
sequence="1"
|
||||
/>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<menuitem id="menu_xf_doc_approval_root" name="Request Approval"
|
||||
web_icon="xf_doc_approval,static/description/icon.png"
|
||||
groups="xf_doc_approval.group_xf_doc_approval_user"/>
|
||||
|
||||
<menuitem id="menu_xf_doc_approval_configuration" name="Configuration"
|
||||
parent="menu_xf_doc_approval_root"
|
||||
groups="xf_doc_approval.group_xf_doc_approval_team_leader,xf_doc_approval.group_xf_doc_approval_manager"
|
||||
sequence="100"/>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="xf_doc_approval_team_form" model="ir.ui.view">
|
||||
<field name="name">xf_doc_approval_team_form</field>
|
||||
<field name="model">xf.doc.approval.team</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<sheet>
|
||||
<group>
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="active"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="user_id"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
</group>
|
||||
</group>
|
||||
<group name="approvers" string="Approvers">
|
||||
<field name="approver_ids" nolabel="1" colspan="2">
|
||||
<list editable="bottom">
|
||||
<field name="step"/>
|
||||
<field name="user_id"/>
|
||||
<field name="role"/>
|
||||
</list>
|
||||
</field>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="xf_doc_approval_team_tree" model="ir.ui.view">
|
||||
<field name="name">xf_doc_approval_team_tree</field>
|
||||
<field name="model">xf.doc.approval.team</field>
|
||||
<field name="arch" type="xml">
|
||||
<list>
|
||||
<field name="active" invisible="True"/>
|
||||
<field name="name"/>
|
||||
<field name="user_id"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_xf_doc_approval_team" model="ir.actions.act_window">
|
||||
<field name="name">Approval Teams</field>
|
||||
<field name="res_model">xf.doc.approval.team</field>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
id="menu_xf_doc_approval_team"
|
||||
action="action_xf_doc_approval_team"
|
||||
parent="menu_xf_doc_approval_configuration"
|
||||
sequence="10"
|
||||
/>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -0,0 +1,24 @@
|
||||
<odoo>
|
||||
<data>
|
||||
<!--
|
||||
<template id="listing">
|
||||
<ul>
|
||||
<li t-foreach="objects" t-as="object">
|
||||
<a t-attf-href="#{ root }/objects/#{ object.id }">
|
||||
<t t-esc="object.display_name"/>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
<template id="object">
|
||||
<h1><t t-esc="object.display_name"/></h1>
|
||||
<dl>
|
||||
<t t-foreach="object._fields" t-as="field">
|
||||
<dt><t t-esc="field"/></dt>
|
||||
<dd><t t-esc="object[field]"/></dd>
|
||||
</t>
|
||||
</dl>
|
||||
</template>
|
||||
-->
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -0,0 +1,60 @@
|
||||
<odoo>
|
||||
<data>
|
||||
<!-- explicit list view definition -->
|
||||
<!--
|
||||
<record model="ir.ui.view" id="xf_doc_approval.list">
|
||||
<field name="name">xf_doc_approval list</field>
|
||||
<field name="model">xf_doc_approval.xf_doc_approval</field>
|
||||
<field name="arch" type="xml">
|
||||
<list>
|
||||
<field name="name"/>
|
||||
<field name="value"/>
|
||||
<field name="value2"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
-->
|
||||
|
||||
<!-- actions opening views on models -->
|
||||
<!--
|
||||
<record model="ir.actions.act_window" id="xf_doc_approval.action_window">
|
||||
<field name="name">xf_doc_approval window</field>
|
||||
<field name="res_model">xf_doc_approval.xf_doc_approval</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
</record>
|
||||
-->
|
||||
|
||||
<!-- server action to the one above -->
|
||||
<!--
|
||||
<record model="ir.actions.server" id="xf_doc_approval.action_server">
|
||||
<field name="name">xf_doc_approval server</field>
|
||||
<field name="model_id" ref="model_xf_doc_approval_xf_doc_approval"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">
|
||||
action = {
|
||||
"type": "ir.actions.act_window",
|
||||
"view_mode": "list,form",
|
||||
"res_model": model._name,
|
||||
}
|
||||
</field>
|
||||
</record>
|
||||
-->
|
||||
|
||||
<!-- Top menu item -->
|
||||
<!--
|
||||
<menuitem name="xf_doc_approval" id="xf_doc_approval.menu_root"/>
|
||||
-->
|
||||
<!-- menu categories -->
|
||||
<!--
|
||||
<menuitem name="Menu 1" id="xf_doc_approval.menu_1" parent="xf_doc_approval.menu_root"/>
|
||||
<menuitem name="Menu 2" id="xf_doc_approval.menu_2" parent="xf_doc_approval.menu_root"/>
|
||||
-->
|
||||
<!-- actions -->
|
||||
<!--
|
||||
<menuitem name="List" id="xf_doc_approval.menu_1_list" parent="xf_doc_approval.menu_1"
|
||||
action="xf_doc_approval.action_window"/>
|
||||
<menuitem name="Server to list" id="xf_doc_approval" parent="xf_doc_approval.menu_2"
|
||||
action="xf_doc_approval.action_server"/>
|
||||
-->
|
||||
</data>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user