Add company top level menu entry, company and employee groups

issue8819
review286291002
This commit is contained in:
Cédric Krier committed 2019-12-05 22:37:16 +01:00
1 parent 6c9580affc
commit c92638ba95
10 files changed
+212 -307

No files matched your search

+3
View File
@@ -1,3 +1,6 @@
* Add company and employee administrator group
* Add top level menu entry
Version 5.4.0 - 2019-11-04
* Bug fixes (see mercurial logs for details)
+19 -15
View File
@@ -2,35 +2,39 @@
# this repository contains the full copyright notices and license terms.
from trytond.pool import Pool
from .company import *
from .cron import *
from . import company
from . import ir
from . import party
from . import res
from .company import CompanyReport
__all__ = ['register', 'CompanyReport']
def register():
Pool.register(
Company,
Employee,
UserEmployee,
User,
Sequence,
SequenceStrict,
Date,
CompanyConfigStart,
Cron,
CronCompany,
company.Company,
company.Employee,
company.CompanyConfigStart,
res.UserEmployee,
res.User,
ir.Sequence,
ir.SequenceStrict,
ir.Date,
ir.Rule,
ir.Cron,
ir.CronCompany,
party.Configuration,
party.PartyConfigurationLang,
party.Party,
party.PartyLang,
party.ContactMechanism,
Rule,
module='company', type_='model')
Pool.register(
CompanyConfig,
company.CompanyConfig,
party.PartyReplace,
party.PartyErase,
module='company', type_='wizard')
Pool.register(
LetterReport,
party.LetterReport,
module='company', type_='report')
+31 -50
View File
@@ -3,11 +3,37 @@
this repository contains the full copyright notices and license terms. -->
<tryton>
<data>
<record model="res.group" id="group_company_admin">
<field name="name">Company Administration</field>
</record>
<record model="res.user-res.group" id="user_admin_company_admin">
<field name="user" ref="res.user_admin"/>
<field name="group" ref="group_company_admin"/>
</record>
<record model="res.group" id="group_employee_admin">
<field name="name">Employee Administration</field>
</record>
<record model="res.user-res.group" id="user_admin_employee_admin">
<field name="user" ref="res.user_admin"/>
<field name="group" ref="group_employee_admin"/>
</record>
<record model="ir.ui.icon" id="company_icon">
<field name="name">tryton-company</field>
<field name="path">icons/tryton-company.svg</field>
</record>
<menuitem name="Company" id="menu_company" sequence="2" icon="tryton-company"/>
<record model="ir.ui.menu-res.group" id="menu_currency_group_company_admin">
<field name="menu" ref="menu_company"/>
<field name="group" ref="group_company_admin"/>
</record>
<record model="ir.ui.menu-res.group" id="menu_currency_group_employee_admin">
<field name="menu" ref="menu_company"/>
<field name="group" ref="group_employee_admin"/>
</record>
<record model="ir.ui.view" id="company_view_form">
<field name="model">company.company</field>
<field name="type">form</field>
@@ -44,7 +70,7 @@ this repository contains the full copyright notices and license terms. -->
<field name="act_window" ref="act_company_tree"/>
</record>
<menuitem parent="party.menu_configuration" action="act_company_tree" icon="tryton-company" id="menu_company_tree"/>
<menuitem parent="menu_company" action="act_company_tree" id="menu_company_tree"/>
<record model="ir.action.act_window" id="act_company_list">
<field name="name">Companies</field>
@@ -62,8 +88,7 @@ this repository contains the full copyright notices and license terms. -->
<field name="view" ref="company_view_form"/>
<field name="act_window" ref="act_company_list"/>
</record>
<menuitem parent="menu_company_tree" action="act_company_list"
id="menu_company_list"/>
<menuitem parent="menu_company_tree" action="act_company_list" id="menu_company_list"/>
<record model="ir.model.access" id="access_company">
<field name="model" search="[('model', '=', 'company.company')]"/>
@@ -74,7 +99,7 @@ this repository contains the full copyright notices and license terms. -->
</record>
<record model="ir.model.access" id="access_company_admin">
<field name="model" search="[('model', '=', 'company.company')]"/>
<field name="group" ref="party.group_party_admin"/>
<field name="group" ref="group_company_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
@@ -92,49 +117,6 @@ this repository contains the full copyright notices and license terms. -->
<field name="name">user_form_preferences</field>
</record>
<record model="ir.ui.view" id="sequence_view_form">
<field name="model">ir.sequence</field>
<field name="inherit" ref="ir.sequence_view_form"/>
<field name="name">sequence_form</field>
</record>
<record model="ir.ui.view" id="sequence_view_tree">
<field name="model">ir.sequence</field>
<field name="inherit" ref="ir.sequence_view_tree"/>
<field name="name">sequence_tree</field>
</record>
<record model="ir.rule.group" id="rule_group_sequence">
<field name="name">User in company</field>
<field name="model" search="[('model', '=', 'ir.sequence')]"/>
<field name="global_p" eval="True"/>
</record>
<record model="ir.rule" id="rule_sequence1">
<field name="domain"
eval="[('company', '=', Eval('user', {}).get('company', None))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_sequence"/>
</record>
<record model="ir.rule" id="rule_sequence2">
<field name="domain" eval="[('company', '=', None)]" pyson="1"/>
<field name="rule_group" ref="rule_group_sequence"/>
</record>
<record model="ir.rule.group" id="rule_group_sequence_strict">
<field name="name">User in company</field>
<field name="model" search="[('model', '=', 'ir.sequence.strict')]"/>
<field name="global_p" eval="True"/>
</record>
<record model="ir.rule" id="rule_sequence_strict1">
<field name="domain"
eval="[('company', '=', Eval('user', {}).get('company', None))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_sequence_strict"/>
</record>
<record model="ir.rule" id="rule_sequence_strict2">
<field name="domain" eval="[('company', '=', None)]" pyson="1"/>
<field name="rule_group" ref="rule_group_sequence_strict"/>
</record>
<record model="ir.ui.view" id="company_config_start_view_form">
<field name="model">company.company.config.start</field>
<field name="type">form</field>
@@ -180,8 +162,7 @@ this repository contains the full copyright notices and license terms. -->
<field name="view" ref="employee_view_form"/>
<field name="act_window" ref="act_employee_form"/>
</record>
<menuitem parent="party.menu_configuration" action="act_employee_form"
id="menu_employee_form"/>
<menuitem parent="menu_company" action="act_employee_form" id="menu_employee_form"/>
<record model="ir.model.access" id="access_employee">
<field name="model" search="[('model', '=', 'company.employee')]"/>
@@ -192,7 +173,7 @@ this repository contains the full copyright notices and license terms. -->
</record>
<record model="ir.model.access" id="access_employee_admin">
<field name="model" search="[('model', '=', 'company.employee')]"/>
<field name="group" ref="party.group_party_admin"/>
<field name="group" ref="group_employee_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
-40
View File
@@ -1,40 +0,0 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.model import ModelSQL, ModelView, fields, dualmethod
from trytond.pool import Pool, PoolMeta
from trytond.transaction import Transaction
__all__ = ['Cron', 'CronCompany']
class Cron(metaclass=PoolMeta):
__name__ = "ir.cron"
companies = fields.Many2Many('ir.cron-company.company', 'cron', 'company',
'Companies', help='Companies registered for this cron')
@dualmethod
@ModelView.button
def run_once(cls, crons):
for cron in crons:
if not cron.companies:
super(Cron, cls).run_once([cron])
else:
for company in cron.companies:
with Transaction().set_context(company=company.id):
super(Cron, cls).run_once([cron])
@staticmethod
def default_companies():
Company = Pool().get('company.company')
return list(map(int, Company.search([])))
class CronCompany(ModelSQL):
'Cron - Company'
__name__ = 'ir.cron-company.company'
_table = 'cron_company_rel'
cron = fields.Many2One('ir.cron', 'Cron', ondelete='CASCADE',
required=True, select=True)
company = fields.Many2One('company.company', 'Company', ondelete='CASCADE',
required=True, select=True)
-12
View File
@@ -1,12 +0,0 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tryton>
<data>
<record model="ir.ui.view" id="cron_view_form">
<field name="model">ir.cron</field>
<field name="inherit" ref="ir.cron_view_form"/>
<field name="name">cron_form</field>
</record>
</data>
</tryton>
+89
View File
@@ -0,0 +1,89 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
import pytz
from trytond.model import ModelSQL, ModelView, fields, dualmethod
from trytond.pool import PoolMeta, Pool
from trytond.pyson import Eval, If
from trytond.transaction import Transaction
class Sequence(metaclass=PoolMeta):
__name__ = 'ir.sequence'
company = fields.Many2One('company.company', 'Company',
domain=[
('id', If(Eval('context', {}).contains('company'), '=', '!='),
Eval('context', {}).get('company', -1)),
], help="Restrict the sequence usage to the company.")
@classmethod
def __setup__(cls):
super(Sequence, cls).__setup__()
cls._order.insert(0, ('company', 'ASC'))
@staticmethod
def default_company():
return Transaction().context.get('company')
class SequenceStrict(Sequence):
__name__ = 'ir.sequence.strict'
class Date(metaclass=PoolMeta):
__name__ = 'ir.date'
@classmethod
def today(cls, timezone=None):
pool = Pool()
Company = pool.get('company.company')
company_id = Transaction().context.get('company')
if timezone is None and company_id:
company = Company(company_id)
if company.timezone:
timezone = pytz.timezone(company.timezone)
return super(Date, cls).today(timezone=timezone)
class Rule(metaclass=PoolMeta):
__name__ = 'ir.rule'
@classmethod
def _get_cache_key(cls):
key = super(Rule, cls)._get_cache_key()
# XXX Use company from context instead of browse to prevent infinite
# loop, but the cache is cleared when User is written.
return key + (Transaction().context.get('company'),
Transaction().context.get('employee'))
class Cron(metaclass=PoolMeta):
__name__ = "ir.cron"
companies = fields.Many2Many('ir.cron-company.company', 'cron', 'company',
'Companies', help='Companies registered for this cron')
@dualmethod
@ModelView.button
def run_once(cls, crons):
for cron in crons:
if not cron.companies:
super(Cron, cls).run_once([cron])
else:
for company in cron.companies:
with Transaction().set_context(company=company.id):
super(Cron, cls).run_once([cron])
@staticmethod
def default_companies():
Company = Pool().get('company.company')
return list(map(int, Company.search([])))
class CronCompany(ModelSQL):
'Cron - Company'
__name__ = 'ir.cron-company.company'
_table = 'cron_company_rel'
cron = fields.Many2One('ir.cron', 'Cron', ondelete='CASCADE',
required=True, select=True)
company = fields.Many2One('company.company', 'Company', ondelete='CASCADE',
required=True, select=True)
+56
View File
@@ -0,0 +1,56 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tryton>
<data>
<record model="ir.ui.view" id="sequence_view_form">
<field name="model">ir.sequence</field>
<field name="inherit" ref="ir.sequence_view_form"/>
<field name="name">sequence_form</field>
</record>
<record model="ir.ui.view" id="sequence_view_tree">
<field name="model">ir.sequence</field>
<field name="inherit" ref="ir.sequence_view_tree"/>
<field name="name">sequence_tree</field>
</record>
<record model="ir.rule.group" id="rule_group_sequence">
<field name="name">User in company</field>
<field name="model" search="[('model', '=', 'ir.sequence')]"/>
<field name="global_p" eval="True"/>
</record>
<record model="ir.rule" id="rule_sequence1">
<field name="domain"
eval="[('company', '=', Eval('user', {}).get('company', None))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_sequence"/>
</record>
<record model="ir.rule" id="rule_sequence2">
<field name="domain" eval="[('company', '=', None)]" pyson="1"/>
<field name="rule_group" ref="rule_group_sequence"/>
</record>
<record model="ir.rule.group" id="rule_group_sequence_strict">
<field name="name">User in company</field>
<field name="model" search="[('model', '=', 'ir.sequence.strict')]"/>
<field name="global_p" eval="True"/>
</record>
<record model="ir.rule" id="rule_sequence_strict1">
<field name="domain"
eval="[('company', '=', Eval('user', {}).get('company', None))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_sequence_strict"/>
</record>
<record model="ir.rule" id="rule_sequence_strict2">
<field name="domain" eval="[('company', '=', None)]" pyson="1"/>
<field name="rule_group" ref="rule_group_sequence_strict"/>
</record>
<record model="ir.ui.view" id="cron_view_form">
<field name="model">ir.cron</field>
<field name="inherit" ref="ir.cron_view_form"/>
<field name="name">cron_form</field>
</record>
</data>
</tryton>
+10
View File
@@ -5,6 +5,7 @@ from trytond.pool import PoolMeta, Pool
from trytond.tools.multivalue import migrate_property
from trytond.transaction import Transaction
from .company import CompanyReport
from .model import CompanyMultiValueMixin, CompanyValueMixin
__all__ = ['Configuration', 'PartyConfigurationLang', 'Party', 'PartyLang',
@@ -110,3 +111,12 @@ class ContactMechanism(CompanyMultiValueMixin, metaclass=PoolMeta):
for address in company.party.addresses:
if address.country:
yield address.country.code
class LetterReport(CompanyReport):
__name__ = 'party.letter'
@classmethod
def execute(cls, ids, data):
with Transaction().set_context(address_with_party=True):
return super(LetterReport, cls).execute(ids, data)
+3 -189
View File
@@ -2,95 +2,10 @@
# this repository contains the full copyright notices and license terms.
import copy
from trytond.model import ModelView, ModelSQL, fields, tree
from trytond.wizard import Wizard, StateView, Button, StateTransition
from trytond.report import Report
from trytond.pyson import Eval, If
from trytond.model import ModelSQL, fields
from trytond.pool import PoolMeta, Pool
from trytond.pyson import Eval
from trytond.transaction import Transaction
from trytond.pool import Pool, PoolMeta
try:
import pytz
TIMEZONES = [(x, x) for x in pytz.common_timezones]
except ImportError:
TIMEZONES = []
TIMEZONES += [(None, '')]
Transaction.cache_keys.update({'company', 'employee'})
__all__ = ['Company', 'Employee', 'UserEmployee', 'User',
'Sequence', 'SequenceStrict', 'Date', 'CompanyConfigStart',
'CompanyConfig', 'CompanyReport', 'LetterReport', 'Rule']
class Company(tree(), ModelSQL, ModelView):
'Company'
__name__ = 'company.company'
party = fields.Many2One('party.party', 'Party', required=True,
ondelete='CASCADE')
parent = fields.Many2One('company.company', 'Parent',
help="Add the company below the parent.")
childs = fields.One2Many('company.company', 'parent', 'Children',
help="Add children below the company.")
header = fields.Text(
'Header', help="The text to display on report headers.")
footer = fields.Text(
'Footer', help="The text to display on report footers.")
currency = fields.Many2One('currency.currency', 'Currency', required=True,
help="The main currency for the company.")
timezone = fields.Selection(TIMEZONES, 'Timezone', translate=False,
help="Used to compute the today date.")
employees = fields.One2Many('company.employee', 'company', 'Employees',
help="Add employees to the company.")
def get_rec_name(self, name):
return self.party.rec_name
@classmethod
def search_rec_name(cls, name, clause):
return [('party.rec_name',) + tuple(clause[1:])]
@classmethod
def write(cls, companies, values, *args):
super(Company, cls).write(companies, values, *args)
# Restart the cache on the domain_get method
Pool().get('ir.rule')._domain_get_cache.clear()
class Employee(ModelSQL, ModelView):
'Employee'
__name__ = 'company.employee'
party = fields.Many2One('party.party', 'Party', required=True,
help="The party which represents the employee.")
company = fields.Many2One('company.company', 'Company', required=True,
help="The company to which the employee belongs.")
start_date = fields.Date('Start Date',
domain=[If((Eval('start_date')) & (Eval('end_date')),
('start_date', '<=', Eval('end_date')),
(),
)
],
depends=['end_date'],
help="When the employee joins the company.")
end_date = fields.Date('End Date',
domain=[If((Eval('start_date')) & (Eval('end_date')),
('end_date', '>=', Eval('start_date')),
(),
)
],
depends=['start_date'],
help="When the employee leaves the company.")
@staticmethod
def default_company():
return Transaction().context.get('company')
def get_rec_name(self, name):
return self.party.rec_name
@classmethod
def search_rec_name(cls, name, clause):
return [('party.rec_name',) + tuple(clause[1:])]
class UserEmployee(ModelSQL):
@@ -284,104 +199,3 @@ class User(metaclass=PoolMeta):
super(User, cls).write(*args)
# Restart the cache on the domain_get method
Rule._domain_get_cache.clear()
class Sequence(metaclass=PoolMeta):
__name__ = 'ir.sequence'
company = fields.Many2One('company.company', 'Company',
domain=[
('id', If(Eval('context', {}).contains('company'), '=', '!='),
Eval('context', {}).get('company', -1)),
], help="Restrict the sequence usage to the company.")
@classmethod
def __setup__(cls):
super(Sequence, cls).__setup__()
cls._order.insert(0, ('company', 'ASC'))
@staticmethod
def default_company():
return Transaction().context.get('company')
class SequenceStrict(Sequence):
__name__ = 'ir.sequence.strict'
class Date(metaclass=PoolMeta):
__name__ = 'ir.date'
@classmethod
def today(cls, timezone=None):
pool = Pool()
Company = pool.get('company.company')
company_id = Transaction().context.get('company')
if timezone is None and company_id:
company = Company(company_id)
if company.timezone:
timezone = pytz.timezone(company.timezone)
return super(Date, cls).today(timezone=timezone)
class CompanyConfigStart(ModelView):
'Company Config'
__name__ = 'company.company.config.start'
class CompanyConfig(Wizard):
'Configure Company'
__name__ = 'company.company.config'
start = StateView('company.company.config.start',
'company.company_config_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('OK', 'company', 'tryton-ok', True),
])
company = StateView('company.company',
'company.company_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Add', 'add', 'tryton-ok', True),
])
add = StateTransition()
def transition_add(self):
User = Pool().get('res.user')
self.company.save()
users = User.search([
('main_company', '=', None),
])
User.write(users, {
'main_company': self.company.id,
'company': self.company.id,
})
return 'end'
class CompanyReport(Report):
@classmethod
def get_context(cls, records, data):
report_context = super(CompanyReport, cls).get_context(records, data)
report_context['company'] = report_context['user'].company
return report_context
class LetterReport(CompanyReport):
__name__ = 'party.letter'
@classmethod
def execute(cls, ids, data):
with Transaction().set_context(address_with_party=True):
return super(LetterReport, cls).execute(ids, data)
class Rule(metaclass=PoolMeta):
__name__ = 'ir.rule'
@classmethod
def _get_cache_key(cls):
key = super(Rule, cls)._get_cache_key()
# XXX Use company from context instead of browse to prevent infinite
# loop, but the cache is cleared when User is written.
return key + (Transaction().context.get('company'),
Transaction().context.get('employee'))
+1 -1
View File
@@ -7,4 +7,4 @@ depends:
res
xml:
company.xml
cron.xml
ir.xml