Added analytic accounts on offices and filters analytic accounts by office on parties, sales and purchases.
This commit refs #19385
This commit is contained in:
parent
c131b50792
commit
ffe270b13c
18
__init__.py
18
__init__.py
|
@ -4,11 +4,16 @@ from trytond.pool import Pool
|
||||||
from . import account
|
from . import account
|
||||||
from . import sale
|
from . import sale
|
||||||
from . import purchase
|
from . import purchase
|
||||||
|
from . import office
|
||||||
|
from . import party
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
Pool.register(
|
Pool.register(
|
||||||
account.Rule,
|
account.Rule,
|
||||||
|
account.AnalyticAccount,
|
||||||
|
office.Office,
|
||||||
|
office.OfficeAnalytic,
|
||||||
module='analytic_office', type_='model')
|
module='analytic_office', type_='model')
|
||||||
Pool.register(
|
Pool.register(
|
||||||
sale.Sale,
|
sale.Sale,
|
||||||
|
@ -21,3 +26,16 @@ def register():
|
||||||
module='analytic_office', type_='model',
|
module='analytic_office', type_='model',
|
||||||
depends=['purchase_office', 'analytic_purchase', 'analytic_apply_rule']
|
depends=['purchase_office', 'analytic_purchase', 'analytic_apply_rule']
|
||||||
)
|
)
|
||||||
|
Pool.register(
|
||||||
|
party.Party,
|
||||||
|
module='analytic_office', type_='model',
|
||||||
|
depends=['analytic_party'])
|
||||||
|
Pool.register(
|
||||||
|
office.SaleLine,
|
||||||
|
module='analytic_office', type_='model',
|
||||||
|
depends=['sale_office', 'analytic_sale', 'analytic_party'])
|
||||||
|
Pool.register(
|
||||||
|
office.PurchaseLine,
|
||||||
|
module='analytic_office', type_='model',
|
||||||
|
depends=['purchase_office', 'analytic_purchase', 'analytic_party']
|
||||||
|
)
|
||||||
|
|
11
account.py
11
account.py
|
@ -16,3 +16,14 @@ class Rule(metaclass=PoolMeta):
|
||||||
('company', '=', Eval('company'))
|
('company', '=', Eval('company'))
|
||||||
],
|
],
|
||||||
depends=['company'])
|
depends=['company'])
|
||||||
|
|
||||||
|
|
||||||
|
class AnalyticAccount(metaclass=PoolMeta):
|
||||||
|
|
||||||
|
__name__ = 'analytic_account.account'
|
||||||
|
|
||||||
|
offices = fields.Many2Many(
|
||||||
|
'company.office-analytic_account.account', 'account', 'office',
|
||||||
|
'Offices',
|
||||||
|
domain=[
|
||||||
|
('company', '=', Eval('context', {}).get('company', -1))])
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
# The COPYRIGHT file at the top level of this repository contains the full
|
||||||
|
# copyright notices and license terms.
|
||||||
|
from trytond.pool import PoolMeta
|
||||||
|
from trytond.model import fields, ModelSQL
|
||||||
|
from trytond.pyson import If, Eval
|
||||||
|
from trytond.transaction import Transaction
|
||||||
|
|
||||||
|
|
||||||
|
class Office(metaclass=PoolMeta):
|
||||||
|
__name__ = 'company.office'
|
||||||
|
|
||||||
|
analytic_accounts = fields.Many2Many(
|
||||||
|
'company.office-analytic_account.account', 'office', 'account',
|
||||||
|
'Analytic Accounts',
|
||||||
|
domain=[
|
||||||
|
('type', '=', 'view'),
|
||||||
|
If(Eval('company', None),
|
||||||
|
('company', '=', Eval('company')),
|
||||||
|
('company', '=', Eval('context', {}).get('company', -1))),
|
||||||
|
],
|
||||||
|
depends=['company'])
|
||||||
|
|
||||||
|
|
||||||
|
class OfficeAnalytic(ModelSQL):
|
||||||
|
"""Office - Analytic Account"""
|
||||||
|
__name__ = 'company.office-analytic_account.account'
|
||||||
|
|
||||||
|
office = fields.Many2One('company.office', 'Office', select=True,
|
||||||
|
ondelete='CASCADE')
|
||||||
|
account = fields.Many2One('analytic_account.account', 'Account',
|
||||||
|
select=True, ondelete='CASCADE')
|
||||||
|
|
||||||
|
|
||||||
|
class SaleLine(metaclass=PoolMeta):
|
||||||
|
__name__ = 'sale.line'
|
||||||
|
|
||||||
|
@fields.depends('sale', '_parent_sale.office')
|
||||||
|
def get_party_analytic_account_domain(self, root):
|
||||||
|
domain = super().get_party_analytic_account_domain(root)
|
||||||
|
if self.sale and self.sale.office:
|
||||||
|
domain.append(('parent.offices', '=', self.sale.office.id))
|
||||||
|
else:
|
||||||
|
domain.append(('parent.offices', '=',
|
||||||
|
Transaction().context.get('office', -1)))
|
||||||
|
return domain
|
||||||
|
|
||||||
|
|
||||||
|
class PurchaseLine(metaclass=PoolMeta):
|
||||||
|
__name__ = 'purchase.line'
|
||||||
|
|
||||||
|
@fields.depends('purchase', '_parent_purchase.office')
|
||||||
|
def get_party_analytic_account_domain(self, root):
|
||||||
|
domain = super().get_party_analytic_account_domain(root)
|
||||||
|
if self.purchase and self.purchase.office:
|
||||||
|
domain.append(('parent.offices', '=', self.purchase.office.id))
|
||||||
|
else:
|
||||||
|
domain.append(('parent.offices', '=',
|
||||||
|
Transaction().context.get('office', -1)))
|
||||||
|
return domain
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!-- The COPYRIGHT file at the top level of this repository contains the full
|
||||||
|
copyright notices and license terms. -->
|
||||||
|
<tryton>
|
||||||
|
<record model="ir.ui.view" id="office_view_form">
|
||||||
|
<field name="model">company.office</field>
|
||||||
|
<field name="inherit" ref="company_office.office_view_form"/>
|
||||||
|
<field name="name">office_form</field>
|
||||||
|
</record>
|
||||||
|
</tryton>
|
|
@ -0,0 +1,19 @@
|
||||||
|
# The COPYRIGHT file at the top level of this repository contains the full
|
||||||
|
# copyright notices and license terms.
|
||||||
|
from trytond.pool import PoolMeta
|
||||||
|
from trytond.pyson import If, Eval
|
||||||
|
|
||||||
|
|
||||||
|
class Party(metaclass=PoolMeta):
|
||||||
|
__name__ = 'party.party'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def __setup__(cls):
|
||||||
|
super().__setup__()
|
||||||
|
cls.analytic_accounts.domain.extend([
|
||||||
|
If(Eval('offices'),
|
||||||
|
('parent.offices', 'in', Eval('offices')), ()),
|
||||||
|
(('parent.offices', 'in',
|
||||||
|
Eval('context', {}).get('offices', [])))])
|
||||||
|
if 'offices' not in cls.analytic_accounts.depends:
|
||||||
|
cls.analytic_accounts.depends.append('offices')
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!-- The COPYRIGHT file at the top level of this repository contains the full
|
||||||
|
copyright notices and license terms. -->
|
||||||
|
<tryton>
|
||||||
|
<data depends="analytic_party">
|
||||||
|
<!-- Office rule -->
|
||||||
|
<record model="ir.rule.group" id="rule_group_party_analytic_account_office">
|
||||||
|
<field name="model"
|
||||||
|
search="[('model', '=', 'party.party-analytic_account.account')]"/>
|
||||||
|
<field name="global_p" eval="True"/>
|
||||||
|
</record>
|
||||||
|
<record model="ir.rule" id="rule_group_party_analytic_account_office1">
|
||||||
|
<field name="domain"
|
||||||
|
eval="[('account.parent.offices', 'in', Eval('user', {}).get('offices', []))]"
|
||||||
|
pyson="1"/>
|
||||||
|
<field name="rule_group" ref="rule_group_party_analytic_account_office"/>
|
||||||
|
</record>
|
||||||
|
<record model="ir.rule" id="rule_group_party_analytic_account_office2">
|
||||||
|
<field name="domain" eval="[('account.parent.offices', '=', None)]" pyson="1"/>
|
||||||
|
<field name="rule_group" ref="rule_group_party_analytic_account_office"/>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</tryton>
|
19
setup.py
19
setup.py
|
@ -67,6 +67,20 @@ dependency_links = {
|
||||||
'branch': branch,
|
'branch': branch,
|
||||||
'series': series
|
'series': series
|
||||||
},
|
},
|
||||||
|
'purchase_office':
|
||||||
|
'git+https://gitlab.com/datalifeit/'
|
||||||
|
'trytond-purchase_office@%(branch)s'
|
||||||
|
'#egg=datalife_purchase_office-%(series)s' % {
|
||||||
|
'branch': branch,
|
||||||
|
'series': series
|
||||||
|
},
|
||||||
|
'analytic_party':
|
||||||
|
'git+https://gitlab.com/datalifeit/'
|
||||||
|
'trytond-analytic_party@%(branch)s'
|
||||||
|
'#egg=datalife_analytic_party-%(series)s' % {
|
||||||
|
'branch': branch,
|
||||||
|
'series': series
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
requires = []
|
requires = []
|
||||||
|
@ -82,8 +96,11 @@ requires.append(get_require_version('trytond'))
|
||||||
tests_require = [
|
tests_require = [
|
||||||
get_require_version('proteus'),
|
get_require_version('proteus'),
|
||||||
get_require_version('trytond_analytic_sale'),
|
get_require_version('trytond_analytic_sale'),
|
||||||
|
get_require_version('trytond_analytic_purchase'),
|
||||||
get_require_version('datalife_analytic_apply_rule'),
|
get_require_version('datalife_analytic_apply_rule'),
|
||||||
get_require_version('datalife_sale_office')
|
get_require_version('datalife_sale_office'),
|
||||||
|
get_require_version('datalife_purchase_office'),
|
||||||
|
get_require_version('datalife_analytic_party')
|
||||||
]
|
]
|
||||||
|
|
||||||
dependency_links = list(dependency_links.values())
|
dependency_links = list(dependency_links.values())
|
||||||
|
|
|
@ -1,161 +1,159 @@
|
||||||
================================
|
========================
|
||||||
Analytic Company Office Scenario
|
Analytic Office Scenario
|
||||||
================================
|
========================
|
||||||
|
|
||||||
Imports::
|
Imports::
|
||||||
|
|
||||||
>>> import datetime
|
>>> from proteus import Model, Wizard
|
||||||
>>> from dateutil.relativedelta import relativedelta
|
>>> from trytond.tests.tools import activate_modules
|
||||||
>>> from decimal import Decimal
|
|
||||||
>>> from operator import attrgetter
|
|
||||||
>>> from proteus import Model, Wizard, Report
|
|
||||||
>>> from trytond.tests.tools import activate_modules, set_user
|
|
||||||
>>> from trytond.modules.company.tests.tools import create_company, \
|
>>> from trytond.modules.company.tests.tools import create_company, \
|
||||||
... get_company
|
... get_company
|
||||||
>>> from trytond.modules.account.tests.tools import create_fiscalyear, \
|
|
||||||
... create_chart, get_accounts
|
|
||||||
>>> from trytond.modules.account_invoice.tests.tools import \
|
|
||||||
... set_fiscalyear_invoice_sequences, create_payment_term
|
|
||||||
>>> today = datetime.date.today()
|
|
||||||
|
|
||||||
Install analytic_office::
|
Install analytic_office::
|
||||||
|
|
||||||
>>> config = activate_modules(['analytic_sale', 'sale_office', 'analytic_office', 'analytic_apply_rule'])
|
>>> config = activate_modules(['analytic_office', 'analytic_party'])
|
||||||
|
|
||||||
|
|
||||||
Create company::
|
Create company::
|
||||||
|
|
||||||
>>> _ = create_company()
|
>>> _ = create_company()
|
||||||
>>> company = get_company()
|
>>> company = get_company()
|
||||||
|
|
||||||
Create sale user::
|
|
||||||
|
Get user::
|
||||||
|
|
||||||
>>> User = Model.get('res.user')
|
>>> User = Model.get('res.user')
|
||||||
>>> Group = Model.get('res.group')
|
>>> admin = User(config.user)
|
||||||
>>> sale_user = User()
|
|
||||||
>>> sale_user.name = 'Sale'
|
|
||||||
>>> sale_user.login = 'sale'
|
|
||||||
>>> sale_user.main_company = company
|
|
||||||
>>> sale_group, = Group.find([('name', '=', 'Sales')])
|
|
||||||
>>> sale_user.groups.append(sale_group)
|
|
||||||
>>> sale_user.save()
|
|
||||||
|
|
||||||
Create fiscal year::
|
|
||||||
|
|
||||||
>>> fiscalyear = set_fiscalyear_invoice_sequences(
|
Create party::
|
||||||
... create_fiscalyear(company))
|
|
||||||
>>> fiscalyear.click('create_period')
|
|
||||||
|
|
||||||
Create chart of accounts::
|
>>> Party = Model.get('party.party')
|
||||||
|
>>> party = Party(name='Party')
|
||||||
|
>>> party.save()
|
||||||
|
|
||||||
>>> _ = create_chart(company)
|
|
||||||
>>> accounts = get_accounts(company)
|
|
||||||
>>> revenue = accounts['revenue']
|
|
||||||
>>> expense = accounts['expense']
|
|
||||||
|
|
||||||
Create branch office::
|
|
||||||
|
|
||||||
>>> Office = Model.get('company.office')
|
|
||||||
>>> office1 = Office(name='Office 1')
|
|
||||||
>>> office1.save()
|
|
||||||
>>> office2 = Office(name='Office 2')
|
|
||||||
>>> office2.save()
|
|
||||||
>>> User = Model.get('res.user')
|
|
||||||
>>> Group = Model.get('res.group')
|
|
||||||
>>> user = User(config.user)
|
|
||||||
>>> user.offices.extend([office1, office2])
|
|
||||||
>>> office1 = Office(office1.id)
|
|
||||||
>>> user.office = office1
|
|
||||||
>>> user.save()
|
|
||||||
>>> set_user(user)
|
|
||||||
>>> config._context = User.get_preferences(True, {})
|
|
||||||
|
|
||||||
Create analytic accounts::
|
Create analytic accounts::
|
||||||
|
|
||||||
>>> AnalyticAccount = Model.get('analytic_account.account')
|
>>> AnalyticAccount = Model.get('analytic_account.account')
|
||||||
>>> root = AnalyticAccount(type='root', name='Root')
|
>>> root = AnalyticAccount()
|
||||||
|
>>> root.name = 'Root'
|
||||||
|
>>> root.code = '1'
|
||||||
|
>>> root.type = 'root'
|
||||||
>>> root.save()
|
>>> root.save()
|
||||||
>>> analytic_account = AnalyticAccount(root=root, parent=root,
|
>>> view_account = AnalyticAccount()
|
||||||
... name='Analytic')
|
>>> view_account.name = 'View 1'
|
||||||
|
>>> view_account.code = '11'
|
||||||
|
>>> view_account.type = 'view'
|
||||||
|
>>> view_account.root = root
|
||||||
|
>>> view_account.parent = root
|
||||||
|
>>> view_account.save()
|
||||||
|
>>> analytic_account = AnalyticAccount()
|
||||||
|
>>> analytic_account.name = 'Analytic 1'
|
||||||
|
>>> analytic_account.code = '111'
|
||||||
|
>>> analytic_account.root = root
|
||||||
|
>>> analytic_account.parent = view_account
|
||||||
>>> analytic_account.save()
|
>>> analytic_account.save()
|
||||||
>>> mandatory_root = AnalyticAccount(type='root', name='Root',
|
>>> view_account2 = AnalyticAccount()
|
||||||
... mandatory=True)
|
>>> view_account2.name = 'View 2'
|
||||||
>>> mandatory_root.save()
|
>>> view_account2.code = '12'
|
||||||
>>> mandatory_analytic_account1 = AnalyticAccount(root=mandatory_root,
|
>>> view_account2.type = 'view'
|
||||||
... parent=mandatory_root, name='Mandatory Analytic 1')
|
>>> view_account2.root = root
|
||||||
>>> mandatory_analytic_account1.save()
|
>>> view_account2.parent = root
|
||||||
>>> mandatory_analytic_account2 = AnalyticAccount(root=mandatory_root,
|
>>> view_account2.save()
|
||||||
... parent=mandatory_root, name='Mandatory Analytic 2')
|
>>> analytic_account2 = AnalyticAccount()
|
||||||
>>> mandatory_analytic_account2.save()
|
>>> analytic_account2.name = 'Analytic 2'
|
||||||
>>> mandatory_analytic_account3 = AnalyticAccount(root=mandatory_root,
|
>>> analytic_account2.code = '121'
|
||||||
... parent=mandatory_root, name='Mandatory Analytic 3')
|
>>> analytic_account2.root = root
|
||||||
>>> mandatory_analytic_account3.save()
|
>>> analytic_account2.parent = view_account2
|
||||||
|
>>> analytic_account2.save()
|
||||||
|
|
||||||
Create parties::
|
|
||||||
|
|
||||||
>>> Party = Model.get('party.party')
|
Create offices::
|
||||||
>>> customer = Party(name='Customer')
|
|
||||||
>>> customer.save()
|
|
||||||
>>> customer2 = Party(name='Customer 2')
|
|
||||||
>>> customer2.save()
|
|
||||||
|
|
||||||
Create analytic rules::
|
>>> Office = Model.get('company.office')
|
||||||
|
>>> office = Office()
|
||||||
|
>>> office.name = 'Office 1'
|
||||||
|
>>> office.company = company
|
||||||
|
>>> office.save()
|
||||||
|
>>> office2 = Office()
|
||||||
|
>>> office2.name = 'Office 2'
|
||||||
|
>>> office2.company = company
|
||||||
|
>>> office2.save()
|
||||||
|
|
||||||
>>> Rule = Model.get('analytic_account.rule')
|
|
||||||
>>> rule = Rule()
|
|
||||||
>>> _, mandatory_entry = rule.analytic_accounts
|
|
||||||
>>> mandatory_entry.root = mandatory_root
|
|
||||||
>>> mandatory_entry.account = mandatory_analytic_account3
|
|
||||||
>>> rule.save()
|
|
||||||
>>> rule = Rule(sale=True, party=customer, office=office2)
|
|
||||||
>>> _, mandatory_entry = rule.analytic_accounts
|
|
||||||
>>> mandatory_entry.root = mandatory_root
|
|
||||||
>>> mandatory_entry.account = mandatory_analytic_account1
|
|
||||||
>>> rule.save()
|
|
||||||
>>> rule = Rule(sale=True, office=office1)
|
|
||||||
>>> _, mandatory_entry = rule.analytic_accounts
|
|
||||||
>>> mandatory_entry.root = mandatory_root
|
|
||||||
>>> mandatory_entry.account = mandatory_analytic_account2
|
|
||||||
>>> rule.save()
|
|
||||||
|
|
||||||
Create account category::
|
Add office to admin user and reload context::
|
||||||
|
|
||||||
>>> ProductCategory = Model.get('product.category')
|
>>> admin.offices.append(office)
|
||||||
>>> account_category = ProductCategory(name="Account Category")
|
>>> office = Office(office.id)
|
||||||
>>> account_category.accounting = True
|
>>> admin.offices.append(office2)
|
||||||
>>> account_category.account_expense = expense
|
>>> office2 = Office(office2.id)
|
||||||
>>> account_category.account_revenue = revenue
|
>>> admin.office = office
|
||||||
>>> account_category.save()
|
>>> admin.save()
|
||||||
|
>>> config._context = User.get_preferences(True, config.context)
|
||||||
|
|
||||||
Create product::
|
|
||||||
|
|
||||||
>>> ProductUom = Model.get('product.uom')
|
Add analytic account to party when account office isn't set::
|
||||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
|
||||||
>>> ProductTemplate = Model.get('product.template')
|
|
||||||
>>> template = ProductTemplate()
|
|
||||||
>>> template.name = 'product'
|
|
||||||
>>> template.default_uom = unit
|
|
||||||
>>> template.type = 'goods'
|
|
||||||
>>> template.salable = True
|
|
||||||
>>> template.list_price = Decimal('10')
|
|
||||||
>>> template.account_category = account_category
|
|
||||||
>>> template.save()
|
|
||||||
>>> product, = template.products
|
|
||||||
|
|
||||||
Create payment term::
|
>>> party.analytic_accounts.append(analytic_account)
|
||||||
|
>>> analytic_account = AnalyticAccount(analytic_account.id)
|
||||||
|
>>> party.save()
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
trytond.exceptions.UserError: The value of the field "Analytic Accounts" on "Party" is not valid according to its domain. -
|
||||||
|
>>> party.reload()
|
||||||
|
|
||||||
>>> payment_term = create_payment_term()
|
|
||||||
>>> payment_term.save()
|
|
||||||
|
|
||||||
Sale with analytic accounts::
|
Add analytic_account to offices::
|
||||||
|
|
||||||
>>> Sale = Model.get('sale.sale')
|
>>> office.analytic_accounts.append(analytic_account)
|
||||||
>>> SaleLine = Model.get('sale.line')
|
>>> analytic_account = AnalyticAccount(analytic_account.id)
|
||||||
>>> sale = Sale()
|
>>> office.save()
|
||||||
>>> sale.party = customer
|
Traceback (most recent call last):
|
||||||
>>> sale.payment_term = payment_term
|
...
|
||||||
>>> sale.office == office1
|
trytond.exceptions.UserError: The value of the field "Analytic Accounts" on "Company branch office" is not valid according to its domain. -
|
||||||
True
|
>>> office.reload()
|
||||||
>>> sale_line = sale.lines.new()
|
>>> office.analytic_accounts.append(view_account)
|
||||||
>>> mandatory_entry, = [a for a in sale_line.analytic_accounts if a.required]
|
>>> view_account = AnalyticAccount(view_account.id)
|
||||||
>>> mandatory_entry.account == mandatory_analytic_account2
|
>>> office.save()
|
||||||
True
|
>>> office2.analytic_accounts.append(view_account2)
|
||||||
|
>>> view_account2 = AnalyticAccount(view_account2.id)
|
||||||
|
>>> office2.save()
|
||||||
|
|
||||||
|
|
||||||
|
Add analytic account Again to party::
|
||||||
|
|
||||||
|
>>> party.analytic_accounts.append(analytic_account)
|
||||||
|
>>> analytic_account = AnalyticAccount(analytic_account.id)
|
||||||
|
>>> party.save()
|
||||||
|
|
||||||
|
|
||||||
|
Add wrong office to party::
|
||||||
|
|
||||||
|
>>> party.offices.append(office2)
|
||||||
|
>>> office2 = Office(office2.id)
|
||||||
|
>>> party.save()
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
trytond.exceptions.UserError: The value of the field "Analytic Accounts" on "Party" is not valid according to its domain. -
|
||||||
|
|
||||||
|
>>> party.reload()
|
||||||
|
|
||||||
|
|
||||||
|
Add office to party::
|
||||||
|
|
||||||
|
>>> party.offices.append(office)
|
||||||
|
>>> office = Office(office.id)
|
||||||
|
>>> party.save()
|
||||||
|
|
||||||
|
|
||||||
|
Add wrong office2 analytic account to party::
|
||||||
|
|
||||||
|
>>> party.analytic_accounts.append(analytic_account2)
|
||||||
|
>>> analytic_account2 = AnalyticAccount(analytic_account2.id)
|
||||||
|
>>> party.save()
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
trytond.exceptions.UserError: The value of the field "Analytic Accounts" on "Party" is not valid according to its domain. -
|
||||||
|
>>> party.reload()
|
||||||
|
|
|
@ -0,0 +1,161 @@
|
||||||
|
================================
|
||||||
|
Analytic Office Rule Scenario
|
||||||
|
================================
|
||||||
|
|
||||||
|
Imports::
|
||||||
|
|
||||||
|
>>> import datetime
|
||||||
|
>>> from dateutil.relativedelta import relativedelta
|
||||||
|
>>> from decimal import Decimal
|
||||||
|
>>> from operator import attrgetter
|
||||||
|
>>> from proteus import Model, Wizard, Report
|
||||||
|
>>> from trytond.tests.tools import activate_modules, set_user
|
||||||
|
>>> from trytond.modules.company.tests.tools import create_company, \
|
||||||
|
... get_company
|
||||||
|
>>> from trytond.modules.account.tests.tools import create_fiscalyear, \
|
||||||
|
... create_chart, get_accounts
|
||||||
|
>>> from trytond.modules.account_invoice.tests.tools import \
|
||||||
|
... set_fiscalyear_invoice_sequences, create_payment_term
|
||||||
|
>>> today = datetime.date.today()
|
||||||
|
|
||||||
|
Install analytic_office::
|
||||||
|
|
||||||
|
>>> config = activate_modules(['analytic_sale', 'sale_office', 'analytic_office', 'analytic_apply_rule'])
|
||||||
|
|
||||||
|
Create company::
|
||||||
|
|
||||||
|
>>> _ = create_company()
|
||||||
|
>>> company = get_company()
|
||||||
|
|
||||||
|
Create sale user::
|
||||||
|
|
||||||
|
>>> User = Model.get('res.user')
|
||||||
|
>>> Group = Model.get('res.group')
|
||||||
|
>>> sale_user = User()
|
||||||
|
>>> sale_user.name = 'Sale'
|
||||||
|
>>> sale_user.login = 'sale'
|
||||||
|
>>> sale_user.main_company = company
|
||||||
|
>>> sale_group, = Group.find([('name', '=', 'Sales')])
|
||||||
|
>>> sale_user.groups.append(sale_group)
|
||||||
|
>>> sale_user.save()
|
||||||
|
|
||||||
|
Create fiscal year::
|
||||||
|
|
||||||
|
>>> fiscalyear = set_fiscalyear_invoice_sequences(
|
||||||
|
... create_fiscalyear(company))
|
||||||
|
>>> fiscalyear.click('create_period')
|
||||||
|
|
||||||
|
Create chart of accounts::
|
||||||
|
|
||||||
|
>>> _ = create_chart(company)
|
||||||
|
>>> accounts = get_accounts(company)
|
||||||
|
>>> revenue = accounts['revenue']
|
||||||
|
>>> expense = accounts['expense']
|
||||||
|
|
||||||
|
Create branch office::
|
||||||
|
|
||||||
|
>>> Office = Model.get('company.office')
|
||||||
|
>>> office1 = Office(name='Office 1')
|
||||||
|
>>> office1.save()
|
||||||
|
>>> office2 = Office(name='Office 2')
|
||||||
|
>>> office2.save()
|
||||||
|
>>> User = Model.get('res.user')
|
||||||
|
>>> Group = Model.get('res.group')
|
||||||
|
>>> user = User(config.user)
|
||||||
|
>>> user.offices.extend([office1, office2])
|
||||||
|
>>> office1 = Office(office1.id)
|
||||||
|
>>> user.office = office1
|
||||||
|
>>> user.save()
|
||||||
|
>>> set_user(user)
|
||||||
|
>>> config._context = User.get_preferences(True, {})
|
||||||
|
|
||||||
|
Create analytic accounts::
|
||||||
|
|
||||||
|
>>> AnalyticAccount = Model.get('analytic_account.account')
|
||||||
|
>>> root = AnalyticAccount(type='root', name='Root')
|
||||||
|
>>> root.save()
|
||||||
|
>>> analytic_account = AnalyticAccount(root=root, parent=root,
|
||||||
|
... name='Analytic')
|
||||||
|
>>> analytic_account.save()
|
||||||
|
>>> mandatory_root = AnalyticAccount(type='root', name='Root',
|
||||||
|
... mandatory=True)
|
||||||
|
>>> mandatory_root.save()
|
||||||
|
>>> mandatory_analytic_account1 = AnalyticAccount(root=mandatory_root,
|
||||||
|
... parent=mandatory_root, name='Mandatory Analytic 1')
|
||||||
|
>>> mandatory_analytic_account1.save()
|
||||||
|
>>> mandatory_analytic_account2 = AnalyticAccount(root=mandatory_root,
|
||||||
|
... parent=mandatory_root, name='Mandatory Analytic 2')
|
||||||
|
>>> mandatory_analytic_account2.save()
|
||||||
|
>>> mandatory_analytic_account3 = AnalyticAccount(root=mandatory_root,
|
||||||
|
... parent=mandatory_root, name='Mandatory Analytic 3')
|
||||||
|
>>> mandatory_analytic_account3.save()
|
||||||
|
|
||||||
|
Create parties::
|
||||||
|
|
||||||
|
>>> Party = Model.get('party.party')
|
||||||
|
>>> customer = Party(name='Customer')
|
||||||
|
>>> customer.save()
|
||||||
|
>>> customer2 = Party(name='Customer 2')
|
||||||
|
>>> customer2.save()
|
||||||
|
|
||||||
|
Create analytic rules::
|
||||||
|
|
||||||
|
>>> Rule = Model.get('analytic_account.rule')
|
||||||
|
>>> rule = Rule()
|
||||||
|
>>> _, mandatory_entry = rule.analytic_accounts
|
||||||
|
>>> mandatory_entry.root = mandatory_root
|
||||||
|
>>> mandatory_entry.account = mandatory_analytic_account3
|
||||||
|
>>> rule.save()
|
||||||
|
>>> rule = Rule(sale=True, party=customer, office=office2)
|
||||||
|
>>> _, mandatory_entry = rule.analytic_accounts
|
||||||
|
>>> mandatory_entry.root = mandatory_root
|
||||||
|
>>> mandatory_entry.account = mandatory_analytic_account1
|
||||||
|
>>> rule.save()
|
||||||
|
>>> rule = Rule(sale=True, office=office1)
|
||||||
|
>>> _, mandatory_entry = rule.analytic_accounts
|
||||||
|
>>> mandatory_entry.root = mandatory_root
|
||||||
|
>>> mandatory_entry.account = mandatory_analytic_account2
|
||||||
|
>>> rule.save()
|
||||||
|
|
||||||
|
Create account category::
|
||||||
|
|
||||||
|
>>> ProductCategory = Model.get('product.category')
|
||||||
|
>>> account_category = ProductCategory(name="Account Category")
|
||||||
|
>>> account_category.accounting = True
|
||||||
|
>>> account_category.account_expense = expense
|
||||||
|
>>> account_category.account_revenue = revenue
|
||||||
|
>>> account_category.save()
|
||||||
|
|
||||||
|
Create product::
|
||||||
|
|
||||||
|
>>> ProductUom = Model.get('product.uom')
|
||||||
|
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||||
|
>>> ProductTemplate = Model.get('product.template')
|
||||||
|
>>> template = ProductTemplate()
|
||||||
|
>>> template.name = 'product'
|
||||||
|
>>> template.default_uom = unit
|
||||||
|
>>> template.type = 'goods'
|
||||||
|
>>> template.salable = True
|
||||||
|
>>> template.list_price = Decimal('10')
|
||||||
|
>>> template.account_category = account_category
|
||||||
|
>>> template.save()
|
||||||
|
>>> product, = template.products
|
||||||
|
|
||||||
|
Create payment term::
|
||||||
|
|
||||||
|
>>> payment_term = create_payment_term()
|
||||||
|
>>> payment_term.save()
|
||||||
|
|
||||||
|
Sale with analytic accounts::
|
||||||
|
|
||||||
|
>>> Sale = Model.get('sale.sale')
|
||||||
|
>>> SaleLine = Model.get('sale.line')
|
||||||
|
>>> sale = Sale()
|
||||||
|
>>> sale.party = customer
|
||||||
|
>>> sale.payment_term = payment_term
|
||||||
|
>>> sale.office == office1
|
||||||
|
True
|
||||||
|
>>> sale_line = sale.lines.new()
|
||||||
|
>>> mandatory_entry, = [a for a in sale_line.analytic_accounts if a.required]
|
||||||
|
>>> mandatory_entry.account == mandatory_analytic_account2
|
||||||
|
True
|
|
@ -0,0 +1,159 @@
|
||||||
|
==========================
|
||||||
|
Analytic Purchase Scenario
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Imports::
|
||||||
|
|
||||||
|
>>> from decimal import Decimal
|
||||||
|
>>> from proteus import Model, Wizard
|
||||||
|
>>> from trytond.tests.tools import activate_modules
|
||||||
|
>>> from trytond.modules.company.tests.tools import create_company, \
|
||||||
|
... get_company
|
||||||
|
|
||||||
|
|
||||||
|
Install analytic_party::
|
||||||
|
|
||||||
|
>>> config = activate_modules(['analytic_office', 'analytic_party',
|
||||||
|
... 'analytic_purchase', 'purchase_office'])
|
||||||
|
|
||||||
|
|
||||||
|
Create company::
|
||||||
|
|
||||||
|
>>> _ = create_company()
|
||||||
|
>>> company = get_company()
|
||||||
|
|
||||||
|
|
||||||
|
Get user::
|
||||||
|
|
||||||
|
>>> User = Model.get('res.user')
|
||||||
|
>>> admin = User(config.user)
|
||||||
|
|
||||||
|
|
||||||
|
Create Supplier::
|
||||||
|
|
||||||
|
>>> Party = Model.get('party.party')
|
||||||
|
>>> supplier = Party(name='Supplier')
|
||||||
|
>>> supplier.save()
|
||||||
|
|
||||||
|
|
||||||
|
Create analytic accounts::
|
||||||
|
|
||||||
|
>>> AnalyticAccount = Model.get('analytic_account.account')
|
||||||
|
>>> root = AnalyticAccount()
|
||||||
|
>>> root.name = 'Root'
|
||||||
|
>>> root.code = '1'
|
||||||
|
>>> root.type = 'root'
|
||||||
|
>>> root.save()
|
||||||
|
>>> view_account = AnalyticAccount()
|
||||||
|
>>> view_account.name = 'View'
|
||||||
|
>>> view_account.code = '11'
|
||||||
|
>>> view_account.type = 'view'
|
||||||
|
>>> view_account.root = root
|
||||||
|
>>> view_account.parent = root
|
||||||
|
>>> view_account.save()
|
||||||
|
>>> view_account2 = AnalyticAccount()
|
||||||
|
>>> view_account2.name = 'View 2'
|
||||||
|
>>> view_account2.code = '12'
|
||||||
|
>>> view_account2.type = 'view'
|
||||||
|
>>> view_account2.root = root
|
||||||
|
>>> view_account2.parent = root
|
||||||
|
>>> view_account2.save()
|
||||||
|
>>> analytic_account = AnalyticAccount()
|
||||||
|
>>> analytic_account.name = 'Analytic 111'
|
||||||
|
>>> analytic_account.code = '111'
|
||||||
|
>>> analytic_account.root = root
|
||||||
|
>>> analytic_account.parent = view_account
|
||||||
|
>>> analytic_account.save()
|
||||||
|
>>> analytic_account2 = AnalyticAccount()
|
||||||
|
>>> analytic_account2.name = 'Analytic 121'
|
||||||
|
>>> analytic_account2.code = '121'
|
||||||
|
>>> analytic_account2.root = root
|
||||||
|
>>> analytic_account2.parent = view_account2
|
||||||
|
>>> analytic_account2.save()
|
||||||
|
|
||||||
|
|
||||||
|
Create office::
|
||||||
|
|
||||||
|
>>> Office = Model.get('company.office')
|
||||||
|
>>> office = Office()
|
||||||
|
>>> office.name = 'Office 1'
|
||||||
|
>>> office.company = company
|
||||||
|
>>> office.save()
|
||||||
|
>>> office2 = Office()
|
||||||
|
>>> office2.name = 'Office 2'
|
||||||
|
>>> office2.company = company
|
||||||
|
>>> office2.save()
|
||||||
|
|
||||||
|
|
||||||
|
Setup offices to users and reload context::
|
||||||
|
|
||||||
|
>>> admin.offices.append(office)
|
||||||
|
>>> office = Office(office.id)
|
||||||
|
>>> admin.offices.append(office2)
|
||||||
|
>>> office2 = Office(office2.id)
|
||||||
|
>>> admin.office = office
|
||||||
|
>>> admin.save()
|
||||||
|
>>> config._context = User.get_preferences(True, config.context)
|
||||||
|
|
||||||
|
|
||||||
|
Add analytic_accounts to offices::
|
||||||
|
|
||||||
|
>>> office.analytic_accounts.append(view_account)
|
||||||
|
>>> view_account = AnalyticAccount(view_account.id)
|
||||||
|
>>> office.save()
|
||||||
|
>>> office2.analytic_accounts.append(view_account2)
|
||||||
|
>>> view_account2 = AnalyticAccount(view_account2.id)
|
||||||
|
>>> office2.save()
|
||||||
|
|
||||||
|
|
||||||
|
Add anaytic accounts to supplier::
|
||||||
|
|
||||||
|
>>> supplier.analytic_accounts.append(analytic_account)
|
||||||
|
>>> analytic_account = AnalyticAccount(analytic_account.id)
|
||||||
|
>>> supplier.analytic_accounts.append(analytic_account2)
|
||||||
|
>>> analytic_account2 = AnalyticAccount(analytic_account2.id)
|
||||||
|
>>> supplier.save()
|
||||||
|
|
||||||
|
|
||||||
|
Create Product::
|
||||||
|
|
||||||
|
>>> ProductUom = Model.get('product.uom')
|
||||||
|
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||||
|
>>> ProductTemplate = Model.get('product.template')
|
||||||
|
>>> template = ProductTemplate()
|
||||||
|
>>> template.name = 'product'
|
||||||
|
>>> template.default_uom = unit
|
||||||
|
>>> template.type = 'service'
|
||||||
|
>>> template.list_price = Decimal('40')
|
||||||
|
>>> template.purchasable = True
|
||||||
|
>>> template.save()
|
||||||
|
>>> product, = template.products
|
||||||
|
|
||||||
|
|
||||||
|
Create Purchase::
|
||||||
|
|
||||||
|
>>> Purchase = Model.get('purchase.purchase')
|
||||||
|
>>> purchase = Purchase()
|
||||||
|
>>> purchase.party = supplier
|
||||||
|
>>> purchase.office = office
|
||||||
|
>>> pline = purchase.lines.new()
|
||||||
|
>>> pline.product = product
|
||||||
|
>>> pline.quantity = 2.0
|
||||||
|
>>> entry_acc, = pline.analytic_accounts
|
||||||
|
>>> entry_acc.account == analytic_account
|
||||||
|
True
|
||||||
|
>>> purchase.save()
|
||||||
|
|
||||||
|
|
||||||
|
Create purchase with office2::
|
||||||
|
|
||||||
|
>>> purchase2 = Purchase()
|
||||||
|
>>> purchase2.party = supplier
|
||||||
|
>>> purchase2.office = office2
|
||||||
|
>>> pline = purchase2.lines.new()
|
||||||
|
>>> pline.product = product
|
||||||
|
>>> pline.quantity = 2.0
|
||||||
|
>>> entry_acc, = pline.analytic_accounts
|
||||||
|
>>> entry_acc.account == analytic_account2
|
||||||
|
True
|
||||||
|
>>> purchase2.save()
|
|
@ -0,0 +1,159 @@
|
||||||
|
======================
|
||||||
|
Analytic Sale Scenario
|
||||||
|
======================
|
||||||
|
|
||||||
|
Imports::
|
||||||
|
|
||||||
|
>>> from decimal import Decimal
|
||||||
|
>>> from proteus import Model, Wizard
|
||||||
|
>>> from trytond.tests.tools import activate_modules
|
||||||
|
>>> from trytond.modules.company.tests.tools import create_company, \
|
||||||
|
... get_company
|
||||||
|
|
||||||
|
|
||||||
|
Install analytic_office::
|
||||||
|
|
||||||
|
>>> config = activate_modules(['analytic_office', 'analytic_party',
|
||||||
|
... 'analytic_sale', 'sale_office'])
|
||||||
|
|
||||||
|
|
||||||
|
Create company::
|
||||||
|
|
||||||
|
>>> _ = create_company()
|
||||||
|
>>> company = get_company()
|
||||||
|
|
||||||
|
|
||||||
|
Get user::
|
||||||
|
|
||||||
|
>>> User = Model.get('res.user')
|
||||||
|
>>> admin = User(config.user)
|
||||||
|
|
||||||
|
|
||||||
|
Create Customer::
|
||||||
|
|
||||||
|
>>> Party = Model.get('party.party')
|
||||||
|
>>> customer = Party(name='Customer')
|
||||||
|
>>> customer.save()
|
||||||
|
|
||||||
|
|
||||||
|
Create analytic accounts::
|
||||||
|
|
||||||
|
>>> AnalyticAccount = Model.get('analytic_account.account')
|
||||||
|
>>> root = AnalyticAccount()
|
||||||
|
>>> root.name = 'Root'
|
||||||
|
>>> root.code = '1'
|
||||||
|
>>> root.type = 'root'
|
||||||
|
>>> root.save()
|
||||||
|
>>> view_account = AnalyticAccount()
|
||||||
|
>>> view_account.name = 'View'
|
||||||
|
>>> view_account.code = '11'
|
||||||
|
>>> view_account.type = 'view'
|
||||||
|
>>> view_account.root = root
|
||||||
|
>>> view_account.parent = root
|
||||||
|
>>> view_account.save()
|
||||||
|
>>> view_account2 = AnalyticAccount()
|
||||||
|
>>> view_account2.name = 'View 2'
|
||||||
|
>>> view_account2.code = '12'
|
||||||
|
>>> view_account2.type = 'view'
|
||||||
|
>>> view_account2.root = root
|
||||||
|
>>> view_account2.parent = root
|
||||||
|
>>> view_account2.save()
|
||||||
|
>>> analytic_account = AnalyticAccount()
|
||||||
|
>>> analytic_account.name = 'Analytic 111'
|
||||||
|
>>> analytic_account.code = '111'
|
||||||
|
>>> analytic_account.root = root
|
||||||
|
>>> analytic_account.parent = view_account
|
||||||
|
>>> analytic_account.save()
|
||||||
|
>>> analytic_account2 = AnalyticAccount()
|
||||||
|
>>> analytic_account2.name = 'Analytic 121'
|
||||||
|
>>> analytic_account2.code = '121'
|
||||||
|
>>> analytic_account2.root = root
|
||||||
|
>>> analytic_account2.parent = view_account2
|
||||||
|
>>> analytic_account2.save()
|
||||||
|
|
||||||
|
|
||||||
|
Create office::
|
||||||
|
|
||||||
|
>>> Office = Model.get('company.office')
|
||||||
|
>>> office = Office()
|
||||||
|
>>> office.name = 'Office 1'
|
||||||
|
>>> office.company = company
|
||||||
|
>>> office.save()
|
||||||
|
>>> office2 = Office()
|
||||||
|
>>> office2.name = 'Office 2'
|
||||||
|
>>> office2.company = company
|
||||||
|
>>> office2.save()
|
||||||
|
|
||||||
|
|
||||||
|
Setup offices to users and reload context::
|
||||||
|
|
||||||
|
>>> admin.offices.append(office)
|
||||||
|
>>> office = Office(office.id)
|
||||||
|
>>> admin.offices.append(office2)
|
||||||
|
>>> office2 = Office(office2.id)
|
||||||
|
>>> admin.office = office
|
||||||
|
>>> admin.save()
|
||||||
|
>>> config._context = User.get_preferences(True, config.context)
|
||||||
|
|
||||||
|
|
||||||
|
Add analytic_accounts to offices::
|
||||||
|
|
||||||
|
>>> office.analytic_accounts.append(view_account)
|
||||||
|
>>> view_account = AnalyticAccount(view_account.id)
|
||||||
|
>>> office.save()
|
||||||
|
>>> office2.analytic_accounts.append(view_account2)
|
||||||
|
>>> view_account2 = AnalyticAccount(view_account2.id)
|
||||||
|
>>> office2.save()
|
||||||
|
|
||||||
|
|
||||||
|
Add anaytic accounts to customer::
|
||||||
|
|
||||||
|
>>> customer.analytic_accounts.append(analytic_account)
|
||||||
|
>>> analytic_account = AnalyticAccount(analytic_account.id)
|
||||||
|
>>> customer.analytic_accounts.append(analytic_account2)
|
||||||
|
>>> analytic_account2 = AnalyticAccount(analytic_account2.id)
|
||||||
|
>>> customer.save()
|
||||||
|
|
||||||
|
|
||||||
|
Create Product::
|
||||||
|
|
||||||
|
>>> ProductUom = Model.get('product.uom')
|
||||||
|
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||||
|
>>> ProductTemplate = Model.get('product.template')
|
||||||
|
>>> template = ProductTemplate()
|
||||||
|
>>> template.name = 'product'
|
||||||
|
>>> template.default_uom = unit
|
||||||
|
>>> template.type = 'service'
|
||||||
|
>>> template.list_price = Decimal('40')
|
||||||
|
>>> template.salable = True
|
||||||
|
>>> template.save()
|
||||||
|
>>> product, = template.products
|
||||||
|
|
||||||
|
|
||||||
|
Create Sale::
|
||||||
|
|
||||||
|
>>> Sale = Model.get('sale.sale')
|
||||||
|
>>> sale = Sale()
|
||||||
|
>>> sale.party = customer
|
||||||
|
>>> sale.office = office
|
||||||
|
>>> sline = sale.lines.new()
|
||||||
|
>>> sline.product = product
|
||||||
|
>>> sline.quantity = 2.0
|
||||||
|
>>> entry_acc, = sline.analytic_accounts
|
||||||
|
>>> entry_acc.account == analytic_account
|
||||||
|
True
|
||||||
|
>>> sale.save()
|
||||||
|
|
||||||
|
|
||||||
|
Create sale with office2::
|
||||||
|
|
||||||
|
>>> sale2 = Sale()
|
||||||
|
>>> sale2.party = customer
|
||||||
|
>>> sale2.office = office2
|
||||||
|
>>> sline = sale2.lines.new()
|
||||||
|
>>> sline.product = product
|
||||||
|
>>> sline.quantity = 2.0
|
||||||
|
>>> entry_acc, = sline.analytic_accounts
|
||||||
|
>>> entry_acc.account == analytic_account2
|
||||||
|
True
|
||||||
|
>>> sale2.save()
|
|
@ -24,4 +24,19 @@ def suite():
|
||||||
tearDown=doctest_teardown, encoding='utf-8',
|
tearDown=doctest_teardown, encoding='utf-8',
|
||||||
checker=doctest_checker,
|
checker=doctest_checker,
|
||||||
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
|
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
|
||||||
|
suite.addTests(doctest.DocFileSuite(
|
||||||
|
'scenario_analytic_office_rule.rst',
|
||||||
|
tearDown=doctest_teardown, encoding='utf-8',
|
||||||
|
checker=doctest_checker,
|
||||||
|
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
|
||||||
|
suite.addTests(doctest.DocFileSuite(
|
||||||
|
'scenario_analytic_sale.rst',
|
||||||
|
tearDown=doctest_teardown, encoding='utf-8',
|
||||||
|
checker=doctest_checker,
|
||||||
|
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
|
||||||
|
suite.addTests(doctest.DocFileSuite(
|
||||||
|
'scenario_analytic_purchase.rst',
|
||||||
|
tearDown=doctest_teardown, encoding='utf-8',
|
||||||
|
checker=doctest_checker,
|
||||||
|
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
|
||||||
return suite
|
return suite
|
||||||
|
|
|
@ -12,7 +12,9 @@ extras_depend:
|
||||||
sale_office
|
sale_office
|
||||||
analytic_purchase
|
analytic_purchase
|
||||||
purchase_office
|
purchase_office
|
||||||
|
analytic_party
|
||||||
|
|
||||||
xml:
|
xml:
|
||||||
account.xml
|
account.xml
|
||||||
|
office.xml
|
||||||
|
party.xml
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!-- The COPYRIGHT file at the top level of this repository contains the full
|
||||||
|
copyright notices and license terms. -->
|
||||||
|
<data>
|
||||||
|
<xpath expr="/form/field[@name='users']" position="replace">
|
||||||
|
<notebook>
|
||||||
|
<page id="general" string="General">
|
||||||
|
<field name="users" colspan="4"/>
|
||||||
|
</page>
|
||||||
|
<page id="analytic" string="Analytic">
|
||||||
|
<field name="analytic_accounts" colspan="4"/>
|
||||||
|
</page>
|
||||||
|
</notebook>
|
||||||
|
</xpath>
|
||||||
|
</data>
|
Loading…
Reference in New Issue