When create a product with or without kit, you need to inform first the main the aprent analytic account. When product is a kit an analytic structure will be create and this structure will be different if the checkbox of analytic by reference are checket or not

This commit is contained in:
Bernat Brunet Torruella 2015-07-21 16:29:24 +02:00
parent 841421a43b
commit c4477405a9
8 changed files with 364 additions and 11 deletions

View file

@ -8,4 +8,6 @@ def register():
Pool.register( Pool.register(
Account, Account,
ProductKitLine, ProductKitLine,
Template,
Product,
module='analytic_product_account', type_='model') module='analytic_product_account', type_='model')

View file

@ -1,16 +1,18 @@
from trytond.model import fields from trytond.model import fields
from trytond.pool import Pool, PoolMeta from trytond.pool import Pool, PoolMeta
from trytond.pyson import Eval, Bool
from trytond.transaction import Transaction from trytond.transaction import Transaction
__metaclass__ = PoolMeta __metaclass__ = PoolMeta
__all__ = ['Account', 'ProductKitLine'] __all__ = ['Account', 'ProductKitLine', 'Template', 'Product']
class Account: class Account:
__name__ = 'analytic_account.account' __name__ = 'analytic_account.account'
kit_line = fields.Many2One('product.kit.line', 'Kit Line') kit_line = fields.Many2One('product.kit.line', 'Kit Line')
parent_kit_line = fields.Many2One('product.product', 'Parent Kit Line')
class ProductKitLine: class ProductKitLine:
@ -41,15 +43,30 @@ class ProductKitLine:
return product_kit_lines return product_kit_lines
def get_missing_analytic_accounts(self): def get_missing_analytic_accounts(self):
existing = {a.parent for a in self.analytic_accounts} res = []
accounts = {a for a in self.parent.template.analytic_accounts.accounts} template = self.parent.template
return [self.get_analytic_account(p) for p in accounts - existing] if not template.parent_analytic_account:
return res
parents = [template.parent_analytic_account]
if template.create_analytic_by_reference:
if not self.parent.parent_analytic_accounts:
parent = self.get_analytic_account(
template.parent_analytic_account,
name=self.parent.template.name)
parent.kit_line = None
parent.parent_kit_line = self.parent
parent.save()
parents = self.parent.parent_analytic_accounts
return [self.get_analytic_account(p) for p in parents]
def get_analytic_account(self, parent): def get_analytic_account(self, parent, name=None):
pool = Pool() pool = Pool()
AnalyticAccount = pool.get('analytic_account.account') AnalyticAccount = pool.get('analytic_account.account')
account = AnalyticAccount() account = AnalyticAccount()
account.name = self.product.template.name if name is None:
account.name = self.product.template.name
else:
account.name = name
account.parent = parent account.parent = parent
account.root = parent.root account.root = parent.root
account.type = 'normal' account.type = 'normal'
@ -108,3 +125,56 @@ class ProductKitLine:
} }
values.append(value) values.append(value)
return values return values
class Template:
__name__ = 'product.template'
parent_analytic_account = fields.Many2One('analytic_account.account',
'Parent Analytic Account')
create_analytic_by_reference = fields.Boolean(
'Create Analytic By Reference', help=('If marked an analytic account'
' will be created for the parent product of the kit'))
@staticmethod
def default_create_analytic_by_reference():
return True
@classmethod
def __setup__(cls):
super(Template, cls).__setup__()
if 'parent_analytic_account' not in cls.analytic_accounts.depends:
states = cls.analytic_accounts.states
readonly = states.get('readonly', False)
cls.analytic_accounts.states.update({
'readonly': (Bool(readonly) |
~Bool(Eval('parent_analytic_account'))),
})
cls.analytic_accounts.depends.append('parent_analytic_account')
class Product:
__name__ = 'product.product'
parent_analytic_accounts = fields.One2Many('analytic_account.account',
'parent_kit_line', 'Analytic Accounts')
analytic_configured = fields.Function(fields.Boolean(
'Analytic Configured'),
'on_change_with_analytic_configured')
@classmethod
def __setup__(cls):
super(Product, cls).__setup__()
if (hasattr(cls, 'kit_lines') and
'analytic_configured' not in cls.kit_lines.depends):
states = cls.kit_lines.states
invisible = states.get('invisible')
cls.kit_lines.states.update({
'invisible': invisible | ~Eval('analytic_configured'),
})
cls.kit_lines.depends.append('analytic_configured')
def on_change_with_analytic_configured(self, name=None):
if self.template and self.template.parent_analytic_account:
return True
return False

13
product.xml Normal file
View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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="template_view_form">
<field name="model">product.template</field>
<field name="inherit" ref="product.template_view_form"/>
<field name="type">form</field>
<field name="name">template_form</field>
</record>
</data>
</tryton>

View file

@ -114,6 +114,9 @@ Create analytic accounts::
>>> analytic_account = AnalyticAccount(root=root, parent=root, >>> analytic_account = AnalyticAccount(root=root, parent=root,
... name='Analytic') ... name='Analytic')
>>> analytic_account.save() >>> analytic_account.save()
>>> reference_analytic_account = AnalyticAccount(root=root, parent=root,
... name='Reference Analytic')
>>> reference_analytic_account.save()
Create parent product:: Create parent product::
@ -134,10 +137,8 @@ Create parent product::
>>> template.cost_price_method = 'fixed' >>> template.cost_price_method = 'fixed'
>>> template.account_expense = expense >>> template.account_expense = expense
>>> template.account_revenue = revenue >>> template.account_revenue = revenue
>>> analytic_selection = AnalyticSelection() >>> template.parent_analytic_account = analytic_account
>>> analytic_selection.accounts.append(analytic_account) >>> template.create_analytic_by_reference = False
>>> analytic_selection.save()
>>> template.analytic_accounts = analytic_selection
>>> template.save() >>> template.save()
>>> product.template = template >>> product.template = template
>>> product.kit = True >>> product.kit = True
@ -194,6 +195,10 @@ Make a kit from parent product and check that analytic accounts are created::
u'Component A' u'Component A'
>>> account_b.name >>> account_b.name
u'Component B' u'Component B'
>>> account_a.parent.name
u'Analytic'
>>> account_b.parent.name
u'Analytic'
If we delete some component analytic account is also deleted:: If we delete some component analytic account is also deleted::
@ -245,3 +250,40 @@ Components can't be deleted if exists entries on their analytic accounts::
... ...
UserError: ('UserError', (u'You cannot delete component "Component A" because it has associated costs.', '')) UserError: ('UserError', (u'You cannot delete component "Component A" because it has associated costs.', ''))
Create analytic accounts with reference::
>>> product = Product()
>>> template = ProductTemplate()
>>> template.name = 'Parent with reference'
>>> template.default_uom = unit
>>> template.type = 'service'
>>> template.purchasable = True
>>> template.salable = True
>>> template.list_price = Decimal('10')
>>> template.cost_price = Decimal('5')
>>> template.cost_price_method = 'fixed'
>>> template.account_expense = expense
>>> template.account_revenue = revenue
>>> template.parent_analytic_account = reference_analytic_account
>>> template.create_analytic_by_reference = True
>>> template.save()
>>> product.template = template
>>> product.kit = True
>>> product.save()
>>> kit_line = product.kit_lines.new()
>>> kit_line.product = component_a
>>> kit_line.quantity = 1
>>> kit_line = product.kit_lines.new()
>>> kit_line.product = component_b
>>> kit_line.quantity = 2
>>> product.save()
>>> reference_analytic_account.reload()
>>> intermediate_account, = reference_analytic_account.childs
>>> intermediate_account.name
u'Parent with reference'
>>> account_a, account_b = intermediate_account.childs
>>> account_a.name
u'Component A'
>>> account_b.name
u'Component B'

View file

@ -0,0 +1,205 @@
==============================
Analytic Product Work Scenario
==============================
=============
General Setup
=============
Imports::
>>> import datetime
>>> from dateutil.relativedelta import relativedelta
>>> from decimal import Decimal
>>> from proteus import config, Model, Wizard
>>> today = datetime.date.today()
Create database::
>>> config = config.set_trytond()
>>> config.pool.test = True
Install modules::
>>> Module = Model.get('ir.module.module')
>>> modules = Module.find([
... ('name', 'in', ['analytic_product_account',
... 'analytic_product_work'])])
>>> Module.install([x.id for x in modules], config.context)
>>> Wizard('ir.module.module.install_upgrade').execute('upgrade')
Create company::
>>> Currency = Model.get('currency.currency')
>>> CurrencyRate = Model.get('currency.currency.rate')
>>> Company = Model.get('company.company')
>>> Party = Model.get('party.party')
>>> company_config = Wizard('company.company.config')
>>> company_config.execute('company')
>>> company = company_config.form
>>> party = Party(name='Dunder Mifflin')
>>> party.save()
>>> company.party = party
>>> currencies = Currency.find([('code', '=', 'USD')])
>>> if not currencies:
... currency = Currency(name='US Dollar', symbol='$', code='USD',
... rounding=Decimal('0.01'), mon_grouping='[3, 3, 0]',
... mon_decimal_point='.')
... currency.save()
... CurrencyRate(date=today + relativedelta(month=1, day=1),
... rate=Decimal('1.0'), currency=currency).save()
... else:
... currency, = currencies
>>> company.currency = currency
>>> company_config.execute('add')
>>> company, = Company.find()
Reload the context::
>>> User = Model.get('res.user')
>>> config._context = User.get_preferences(True, config.context)
Create chart of accounts::
>>> AccountTemplate = Model.get('account.account.template')
>>> Account = Model.get('account.account')
>>> account_template, = AccountTemplate.find([('parent', '=', None)])
>>> create_chart = Wizard('account.create_chart')
>>> create_chart.execute('account')
>>> create_chart.form.account_template = account_template
>>> create_chart.form.company = company
>>> create_chart.execute('create_account')
>>> receivable, = Account.find([
... ('kind', '=', 'receivable'),
... ('company', '=', company.id),
... ])
>>> payable, = Account.find([
... ('kind', '=', 'payable'),
... ('company', '=', company.id),
... ])
>>> revenue, = Account.find([
... ('kind', '=', 'revenue'),
... ('company', '=', company.id),
... ])
>>> expense, = Account.find([
... ('kind', '=', 'expense'),
... ('company', '=', company.id),
... ])
>>> create_chart.form.account_receivable = receivable
>>> create_chart.form.account_payable = payable
>>> create_chart.execute('create_properties')
Create fiscal year::
>>> FiscalYear = Model.get('account.fiscalyear')
>>> Sequence = Model.get('ir.sequence')
>>> SequenceStrict = Model.get('ir.sequence.strict')
>>> fiscalyear = FiscalYear(name='%s' % today.year)
>>> fiscalyear.start_date = today + relativedelta(month=1, day=1)
>>> fiscalyear.end_date = today + relativedelta(month=12, day=31)
>>> fiscalyear.company = company
>>> post_move_sequence = Sequence(name='%s' % today.year,
... code='account.move', company=company)
>>> post_move_sequence.save()
>>> fiscalyear.post_move_sequence = post_move_sequence
>>> fiscalyear.save()
>>> FiscalYear.create_period([fiscalyear.id], config.context)
>>> period = fiscalyear.periods[0]
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()
>>> reference_analytic_account = AnalyticAccount(root=root, parent=root,
... name='Reference Analytic')
>>> reference_analytic_account.save()
Create parent product::
>>> ProductUom = Model.get('product.uom')
>>> ProductTemplate = Model.get('product.template')
>>> Product = Model.get('product.product')
>>> AnalyticSelection = Model.get('analytic_account.account.selection')
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
>>> product = Product()
>>> template = ProductTemplate()
>>> template.name = 'Parent'
>>> template.default_uom = unit
>>> template.type = 'service'
>>> template.purchasable = True
>>> template.salable = True
>>> template.list_price = Decimal('10')
>>> template.cost_price = Decimal('5')
>>> template.cost_price_method = 'fixed'
>>> template.account_expense = expense
>>> template.account_revenue = revenue
>>> template.parent_analytic_account = analytic_account
>>> template.create_analytic_by_reference = False
>>> template.save()
>>> product.template = template
>>> product.kit = True
>>> product.save()
Create component A (service) and component B (good)::
>>> component_a = Product()
>>> template = ProductTemplate()
>>> template.name = 'Component A'
>>> template.default_uom = unit
>>> template.type = 'service'
>>> template.purchasable = True
>>> template.salable = True
>>> template.list_price = Decimal('10')
>>> template.cost_price = Decimal('5')
>>> template.cost_price_method = 'fixed'
>>> template.account_expense = expense
>>> template.account_revenue = revenue
>>> template.save()
>>> component_a.template = template
>>> component_a.save()
>>> component_b = Product()
>>> template = ProductTemplate()
>>> template.name = 'Component B'
>>> template.default_uom = unit
>>> template.type = 'goods'
>>> template.purchasable = True
>>> template.salable = True
>>> template.list_price = Decimal('10')
>>> template.cost_price = Decimal('5')
>>> template.cost_price_method = 'fixed'
>>> template.account_expense = expense
>>> template.account_revenue = revenue
>>> template.save()
>>> component_b.template = template
>>> component_b.save()
Make a kit from parent product and check that analytic accounts are created::
>>> len(analytic_account.childs)
0
>>> kit_line = product.kit_lines.new()
>>> kit_line.product = component_a
>>> kit_line.quantity = 1
>>> kit_line = product.kit_lines.new()
>>> kit_line.product = component_b
>>> kit_line.quantity = 2
>>> product.save()
>>> analytic_account.reload()
>>> account_a, account_b = analytic_account.childs
>>> account_a.name
u'Component A'
>>> account_b.name
u'Component B'
>>> kit_component_a, kit_component_b = product.kit_lines
>>> component_a_work, = kit_component_a.product.works
>>> component_b_work, = kit_component_b.product.works
>>> component_a_work.rec_name
u'Root\\Analytic\\Component A\\Component A'
>>> component_b_work.rec_name
u'Root\\Analytic\\Component B\\Component B'

View file

@ -3,7 +3,7 @@
import unittest import unittest
import doctest import doctest
import trytond.tests.test_tryton import trytond.tests.test_tryton
from trytond.tests.test_tryton import test_depends from trytond.tests.test_tryton import test_view, test_depends
from trytond.tests.test_tryton import doctest_setup, doctest_teardown from trytond.tests.test_tryton import doctest_setup, doctest_teardown
@ -13,6 +13,10 @@ class TestCase(unittest.TestCase):
def setUp(self): def setUp(self):
trytond.tests.test_tryton.install_module('analytic_product_account') trytond.tests.test_tryton.install_module('analytic_product_account')
def test0005views(self):
'Test views'
test_view('analytic_product_account')
def test0006depends(self): def test0006depends(self):
'Test depends' 'Test depends'
test_depends() test_depends()
@ -25,4 +29,8 @@ def suite():
'scenario_analytic_product_account.rst', 'scenario_analytic_product_account.rst',
setUp=doctest_setup, tearDown=doctest_teardown, encoding='utf-8', setUp=doctest_setup, tearDown=doctest_teardown, encoding='utf-8',
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)) optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
suite.addTests(doctest.DocFileSuite(
'scenario_analytic_product_work.rst',
setUp=doctest_setup, tearDown=doctest_teardown, encoding='utf-8',
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
return suite return suite

View file

@ -6,3 +6,4 @@ depends:
extras_depend: extras_depend:
analytic_product_work analytic_product_work
xml: xml:
product.xml

12
view/template_form.xml Normal file
View file

@ -0,0 +1,12 @@
<?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/notebook/page[@id='accounting']/group[@name='analytic_accounts']"
position="before">
<label name="parent_analytic_account"/>
<field name="parent_analytic_account"/>
<label name="create_analytic_by_reference"/>
<field name="create_analytic_by_reference"/>
</xpath>
</data>