diff --git a/account_usability/account_view.xml b/account_usability/account_view.xml
index dea527d..abc58ef 100644
--- a/account_usability/account_view.xml
+++ b/account_usability/account_view.xml
@@ -374,8 +374,9 @@ module -->
account.move.line
-
-
+
+ 1
+
diff --git a/commission_simple/__init__.py b/commission_simple/__init__.py
new file mode 100644
index 0000000..35e7c96
--- /dev/null
+++ b/commission_simple/__init__.py
@@ -0,0 +1,4 @@
+# -*- coding: utf-8 -*-
+
+from . import models
+from . import wizard
diff --git a/commission_simple/__manifest__.py b/commission_simple/__manifest__.py
new file mode 100644
index 0000000..83acfa0
--- /dev/null
+++ b/commission_simple/__manifest__.py
@@ -0,0 +1,47 @@
+# -*- coding: utf-8 -*-
+# Copyright 2019 Akretion France (http://www.akretion.com)
+# @author Alexis de Lattre
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+{
+ 'name': 'Commission Simple',
+ 'version': '10.0.1.0.0',
+ 'category': 'Sales',
+ 'license': 'AGPL-3',
+ 'summary': 'Compute commissions for salesman',
+ 'description': """
+Commission Simple
+=================
+
+This module is a **simple** module to compute commission for salesman. From my experience, companies often use very specific methods to compute commissions and it's impossible to develop a module that can support all of them. So the goal of this module is just to have a simple base to build the company-specific commissionning system by inheriting this simple module.
+
+Here is a short description of this module:
+
+* create commission profiles using rules (per product category, per product, per product and customer, etc.),
+* the commission rules can have a start and end date (optional),
+* commissionning can happen on invoicing or on payment,
+* each invoice line can only be commissionned to one salesman,
+* commission reports are stored in Odoo.
+
+This module has been written by Alexis de Lattre from Akretion
+.
+ """,
+ 'author': 'Akretion',
+ 'website': 'http://www.akretion.com',
+ 'depends': [
+ 'account',
+ 'date_range',
+ # this uses some related fields on account.invoice.line
+ 'account_usability',
+ ],
+ 'data': [
+ 'data/decimal_precision.xml',
+ 'views/commission.xml',
+ 'views/res_users.xml',
+ 'views/account_config_settings.xml',
+ 'wizard/commission_compute_view.xml',
+ 'security/ir.model.access.csv',
+ 'security/rule.xml',
+ ],
+ 'installable': True,
+}
diff --git a/commission_simple/data/decimal_precision.xml b/commission_simple/data/decimal_precision.xml
new file mode 100644
index 0000000..5878b9d
--- /dev/null
+++ b/commission_simple/data/decimal_precision.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+ Commission Rate
+ 2
+
+
+
+
diff --git a/commission_simple/models/__init__.py b/commission_simple/models/__init__.py
new file mode 100644
index 0000000..789deef
--- /dev/null
+++ b/commission_simple/models/__init__.py
@@ -0,0 +1,7 @@
+# -*- coding: utf-8 -*-
+
+from . import commission
+from . import res_users
+from . import res_company
+from . import account_config_settings
+from . import account_invoice_line
diff --git a/commission_simple/models/account_config_settings.py b/commission_simple/models/account_config_settings.py
new file mode 100644
index 0000000..5d959d4
--- /dev/null
+++ b/commission_simple/models/account_config_settings.py
@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+# Copyright 2019 Akretion France (http://www.akretion.com/)
+# @author: Alexis de Lattre
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from odoo import fields, models
+
+
+class AccountConfigSettings(models.TransientModel):
+ _inherit = 'account.config.settings'
+
+ commission_date_range_type_id = fields.Many2one(
+ related='company_id.commission_date_range_type_id', readonly=False)
diff --git a/commission_simple/models/account_invoice_line.py b/commission_simple/models/account_invoice_line.py
new file mode 100644
index 0000000..4d78052
--- /dev/null
+++ b/commission_simple/models/account_invoice_line.py
@@ -0,0 +1,108 @@
+# -*- coding: utf-8 -*-
+# Copyright 2019 Akretion France (http://www.akretion.com/)
+# @author Alexis de Lattre
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+
+from odoo import api, fields, models
+import odoo.addons.decimal_precision as dp
+
+
+class AccountInvoiceLine(models.Model):
+ _inherit = 'account.invoice.line'
+
+ user_id = fields.Many2one(
+ related='invoice_id.user_id', store=True, readonly=True)
+ product_categ_id = fields.Many2one(
+ related='product_id.categ_id', store=True, readonly=True)
+ commission_result_id = fields.Many2one(
+ 'commission.result', string='Commission Result')
+ commission_rule_id = fields.Many2one(
+ 'commission.rule', 'Matched Commission Rule', ondelete='restrict')
+ commission_base = fields.Monetary('Commission Base', currency_field='company_currency_id')
+ commission_rate = fields.Float('Commission Rate', digits=dp.get_precision('Commission Rate'))
+ commission_amount = fields.Monetary(
+ string='Commission Amount', currency_field='company_currency_id',
+ readonly=True, compute='_compute_commission_amount', store=True)
+
+ @api.depends('commission_rate', 'commission_base')
+ def _compute_amount(self):
+ for line in self:
+ line.commission_amount = line.commission_rate * line.commission_base / 100.0
+
+ def compute_commission_for_one_user(self, user, date_range, rules):
+ profile = user.commission_profile_id
+ assert profile
+ domain = [
+ ('invoice_type', 'in', ('out_invoice', 'out_refund')),
+ ('date_invoice', '<=', date_range.date_end),
+ ('company_id', '=', self.env.user.company_id.id),
+ ('user_id', '=', user.id),
+ ('commission_result_id', '=', False),
+ ]
+ if profile.trigger_type == 'invoice':
+ domain.append(('state', 'in', ('open', 'paid')))
+ elif profile.trigger_type == 'payment':
+ # TODO : for this trigger, we would need to filter
+ # out the invoices paid after the end date of the period compute
+ domain.append(('state', '=', 'paid'))
+ else:
+ raise
+ ilines = self.search(domain, order='date_invoice, invoice_id, sequence')
+ com_result = self.env['commission.result'].create({
+ 'user_id': user.id,
+ 'profile_id': profile.id,
+ 'date_range_id': date_range.id,
+ })
+ total = 0.0
+ for iline in ilines:
+ rule = iline._match_commission_rule(rules[profile.id])
+ if rule:
+ lvals = iline._prepare_commission_data(rule, com_result)
+ if lvals:
+ iline.write(lvals)
+ total += lvals['commission_amount']
+ com_result.amount_total = total
+ return com_result
+
+ def _match_commission_rule(self, rules):
+ # commission rules are already in the right order
+ self.ensure_one()
+ for rule in rules:
+ if rule['date_start'] and rule['date_start'] > self.date_invoice:
+ continue
+ if rule['date_end'] and rule['date_end'] < self.date_invoice:
+ continue
+ if rule['applied_on'] == '0_customer_product':
+ if (
+ self.commercial_partner_id.id in
+ rule['partner_ids'] and
+ self.product_id.id in rule['product_ids']):
+ return rule
+ elif rule['applied_on'] == '1_customer_product_category':
+ if (
+ self.commercial_partner_id.id in
+ rule['partner_ids'] and
+ self.product_categ_id.id in rule['product_categ_ids']):
+ return rule
+ elif rule['applied_on'] == '2_product':
+ if self.product_id.id in rule['product_ids']:
+ return rule
+ elif rule['applied_on'] == '3_product_category':
+ if self.product_categ_id.id in rule['product_categ_ids']:
+ return rule
+ elif rule['applied_on'] == '4_global':
+ return rule
+ return False
+
+ def _prepare_commission_data(self, rule, commission_result):
+ self.ensure_one()
+ lvals = {
+ 'commission_result_id': commission_result.id,
+ 'commission_rule_id': rule['id'],
+ # company currency
+ 'commission_base': self.price_subtotal_signed,
+ 'commission_rate': rule['rate'],
+ 'commission_amount': rule['rate'] * self.price_subtotal_signed,
+ }
+ return lvals
diff --git a/commission_simple/models/commission.py b/commission_simple/models/commission.py
new file mode 100644
index 0000000..b1a7182
--- /dev/null
+++ b/commission_simple/models/commission.py
@@ -0,0 +1,120 @@
+# -*- coding: utf-8 -*-
+# Copyright Akretion France (http://www.akretion.com/)
+# @author Alexis de Lattre
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+
+from odoo import fields, models, api
+import odoo.addons.decimal_precision as dp
+
+
+class CommissionProfile(models.Model):
+ _name = 'commission.profile'
+ _description = 'Commission Profile'
+
+ name = fields.Char(string='Name of the Profile', required=True)
+ active = fields.Boolean(string='Active', default=True)
+ company_id = fields.Many2one(
+ 'res.company', string='Company',
+ required=True,
+ default=lambda self: self.env['res.company']._company_default_get())
+ line_ids = fields.One2many(
+ 'commission.rule', 'profile_id', string='Commission Rules')
+ trigger_type = fields.Selection([
+ ('invoice', 'Invoicing'),
+ ('payment', 'Payment'),
+ ], default='invoice', string='Trigger', required=True)
+
+
+class CommissionRule(models.Model):
+ _name = 'commission.rule'
+ _description = 'Commission Rule'
+ _order = 'profile_id, applied_on'
+
+ partner_ids = fields.Many2many(
+ 'res.partner', string='Customers',
+ domain=[('parent_id', '=', False), ('customer', '=', True)])
+ product_categ_ids = fields.Many2many(
+ 'product.category', string="Product Categories",
+ domain=[('type', '=', 'normal')])
+ product_ids = fields.Many2many('product.product', string='Products')
+ date_start = fields.Date('Start Date')
+ date_end = fields.Date('End Date')
+ profile_id = fields.Many2one(
+ 'commission.profile', string='Profile', ondelete='cascade')
+ company_id = fields.Many2one(
+ related='profile_id.company_id', store=True, readonly=True)
+ rate = fields.Float(
+ 'Commission Rate', digits=dp.get_precision('Commission Rate'),
+ copy=False)
+ applied_on = fields.Selection([
+ ('0_customer_product', 'Products and Customers'),
+ ('1_customer_product_category', "Product Categories and Customers"),
+ ('2_product', "Products"),
+ ('3_product_category', "Product Categories"),
+ ('4_global', u'Global')],
+ string='Apply On', default='4_global', required=True)
+ active = fields.Boolean(string='Active', default=True)
+
+ @api.model
+ def load_all_rules(self):
+ rules = self.search_read()
+ res = {} # key = profile, value = [rule1 recordset, rule2]
+ for rule in rules:
+ if rule['profile_id']:
+ if rule['profile_id'][0] not in res:
+ res[rule['profile_id'][0]] = [rule]
+ else:
+ res[rule['profile_id'][0]].append(rule)
+ return res
+
+ _sql_constraints = [(
+ 'rate_positive',
+ 'CHECK(rate >= 0)',
+ 'Rate must be positive !')]
+
+
+class CommissionResult(models.Model):
+ _name = 'commission.result'
+ _description = "Commission Result"
+ _order = 'date_start desc'
+
+ user_id = fields.Many2one(
+ 'res.users', 'Salesman', required=True, ondelete='restrict',
+ readonly=True)
+ profile_id = fields.Many2one(
+ 'commission.profile', string='Commission Profile',
+ readonly=True)
+ company_id = fields.Many2one(
+ 'res.company', string='Company',
+ required=True, readonly=True,
+ default=lambda self: self.env['res.company']._company_default_get())
+ company_currency_id = fields.Many2one(
+ related='company_id.currency_id', string='Company Currency',
+ readonly=True, store=True)
+ date_range_id = fields.Many2one(
+ 'date.range', required=True, string='Period', readonly=True)
+ date_start = fields.Date(
+ related='date_range_id.date_start', readonly=True, store=True)
+ date_end = fields.Date(
+ related='date_range_id.date_end', readonly=True, store=True)
+ line_ids = fields.One2many(
+ 'account.invoice.line', 'commission_result_id', 'Commission Lines',
+ readonly=True)
+ amount_total = fields.Monetary(
+ string='Commission Total', currency_field='company_currency_id',
+ help='This is the total amount at the date of the computation of the commission',
+ readonly=True)
+
+ def name_get(self):
+ res = []
+ for result in self:
+ name = '%s (%s)' % (result.user_id.name, result.date_range_id.name)
+ res.append((result.id, name))
+ return res
+
+ _sql_constraints = [(
+ 'salesman_period_company_unique',
+ 'unique(company_id, commission_partner_id, date_range_id)',
+ 'A commission result already exists for this salesman for '
+ 'the same period')]
diff --git a/commission_simple/models/res_company.py b/commission_simple/models/res_company.py
new file mode 100644
index 0000000..f77ac36
--- /dev/null
+++ b/commission_simple/models/res_company.py
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+# Copyright 2019 Akretion France (http://www.akretion.com/)
+# @author Alexis de Lattre
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+
+from odoo import fields, models
+
+
+class ResCompany(models.Model):
+ _inherit = 'res.company'
+
+ commission_date_range_type_id = fields.Many2one(
+ 'date.range.type', string='Commission Periodicity')
diff --git a/commission_simple/models/res_users.py b/commission_simple/models/res_users.py
new file mode 100644
index 0000000..b0bebe4
--- /dev/null
+++ b/commission_simple/models/res_users.py
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+# Copyright 2019 Akretion France (http://www.akretion.com/)
+# @author Alexis de Lattre
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+
+from odoo import fields, models
+
+
+class ResUsers(models.Model):
+ _inherit = 'res.users'
+
+ commission_profile_id = fields.Many2one(
+ 'commission.profile', string='Commission Profile',
+ company_dependant=True)
diff --git a/commission_simple/security/ir.model.access.csv b/commission_simple/security/ir.model.access.csv
new file mode 100644
index 0000000..350af85
--- /dev/null
+++ b/commission_simple/security/ir.model.access.csv
@@ -0,0 +1,7 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_commission_profile_read,Read access on commission.profile for employees,model_commission_profile,base.group_user,1,0,0,0
+access_commission_profile_full,Full access on commission.profile for financial manager,model_commission_profile,account.group_account_manager,1,1,1,1
+access_commission_rule_full,Full access on commission.rule for financial manager,model_commission_rule,account.group_account_manager,1,1,1,1
+access_commission_rule_read,Read access on commission.rule for invoicing group,model_commission_rule,account.group_account_invoice,1,0,0,0
+access_commission_result_full,Full access on commission.result to accountant,model_commission_result,account.group_account_user,1,1,1,1
+access_commission_result_read,Read access on commission.result to invoicing grp,model_commission_result,account.group_account_invoice,1,0,0,0
diff --git a/commission_simple/security/rule.xml b/commission_simple/security/rule.xml
new file mode 100644
index 0000000..b9bee43
--- /dev/null
+++ b/commission_simple/security/rule.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+ Commission Profile multi-company
+
+ ['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id])]
+
+
+
+ Commission Rule multi-company
+
+ ['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id])]
+
+
+
+ Commission Result multi-company
+
+ ['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id])]
+
+
+
+
diff --git a/commission_simple/views/account_config_settings.xml b/commission_simple/views/account_config_settings.xml
new file mode 100644
index 0000000..1dbf87d
--- /dev/null
+++ b/commission_simple/views/account_config_settings.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+ commission.account.config.settings.form
+ account.config.settings
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/commission_simple/views/commission.xml b/commission_simple/views/commission.xml
new file mode 100644
index 0000000..4c9aa95
--- /dev/null
+++ b/commission_simple/views/commission.xml
@@ -0,0 +1,211 @@
+
+
+
+
+
+
+
+
+
+
+ commission.profile.form
+ commission.profile
+
+
+
+
+
+
+ commission.profile.tree
+ commission.profile
+
+
+
+
+
+
+
+
+
+
+ Commission Profiles
+ commission.profile
+ tree,form
+
+
+
+
+
+
+
+ commission.rule.form
+ commission.rule
+
+
+
+
+
+
+ commission.rule.tree
+ commission.rule
+
+
+
+
+
+
+
+
+
+
+
+
+ commission.rule.search
+ commission.rule
+
+
+
+
+
+
+
+
+
+
+
+
+ Commission Rules
+ commission.rule
+ tree,form
+ {'commission_rule_main_view': True}
+
+
+
+
+
+
+
+ commission.result.form
+ commission.result
+
+
+
+
+
+
+ commission.result.tree
+ commission.result
+
+
+
+
+
+
+
+
+
+
+
+
+
+ commission.result.search
+ commission.result
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Commissions
+ commission.result
+ tree,form
+
+
+
+
+
+
diff --git a/commission_simple/views/res_users.xml b/commission_simple/views/res_users.xml
new file mode 100644
index 0000000..9d6a447
--- /dev/null
+++ b/commission_simple/views/res_users.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+ commission.res.users.form
+ res.users
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/commission_simple/wizard/__init__.py b/commission_simple/wizard/__init__.py
new file mode 100644
index 0000000..cd25b88
--- /dev/null
+++ b/commission_simple/wizard/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+from . import commission_compute
diff --git a/commission_simple/wizard/commission_compute.py b/commission_simple/wizard/commission_compute.py
new file mode 100644
index 0000000..f3a8fec
--- /dev/null
+++ b/commission_simple/wizard/commission_compute.py
@@ -0,0 +1,78 @@
+# -*- coding: utf-8 -*-
+# Copyright 2019 Akretion France (http://www.akretion.com)
+# @author Alexis de Lattre
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from odoo import api, fields, models, _
+from dateutil.relativedelta import relativedelta
+from odoo.exceptions import UserError
+import logging
+logger = logging.getLogger(__name__)
+
+
+class CommissionCompute(models.TransientModel):
+ _name = 'commission.compute'
+ _description = 'Compute Commissoins'
+
+ @api.model
+ def _default_date_range(self):
+ drange_type = self.env.user.company_id.commission_date_range_type_id
+ if not drange_type:
+ return False
+ today = fields.Date.from_string(fields.Date.context_today(self))
+ first_day_last_month = today + relativedelta(months=-1, day=1)
+ dranges = self.env['date.range'].search([
+ ('company_id', '=', self.env.user.company_id.id),
+ ('type_id', '=', drange_type.id),
+ ('date_start', '=', fields.Date.to_string(first_day_last_month))
+ ])
+ return dranges and dranges[0] or dranges
+
+ date_range_id = fields.Many2one(
+ 'date.range', required=True, string='Period',
+ default=lambda self: self._default_date_range())
+ date_start = fields.Date(related='date_range_id.date_start', readonly=True)
+ date_end = fields.Date(related='date_range_id.date_end', readonly=True)
+
+ def run(self):
+ self.ensure_one()
+ creso = self.env['commission.result']
+ ruo = self.env['res.users']
+ date_range = self.date_range_id
+ existing_res = creso.search([('date_range_id', '=', date_range.id)])
+ if existing_res:
+ raise UserError(
+ u'Il existe déjà des commissions pour cette période.')
+ com_result_ids = self.core_compute()
+ if not com_result_ids:
+ raise UserError(_('No commission generated.'))
+ action = self.env['ir.actions.act_window'].for_xml_id(
+ 'commission_simple', 'commission_result_action')
+ action.update({
+ 'views': False,
+ 'domain': "[('id', 'in', %s)]" % com_result_ids,
+ })
+ return action
+
+ def core_compute(self):
+ rules = self.env['commission.rule'].load_all_rules()
+ ailo = self.env['account.invoice.line']
+ ruo = self.env['res.users']
+ com_result_ids = []
+ for user in ruo.with_context(active_test=False).search([]):
+ if user.commission_profile_id:
+ if user.commission_profile_id.id not in rules:
+ raise UserError(_(
+ "The commission profile '%s' doesn't have any rules.")
+ % user.commission_profile_id.name)
+ com_result = ailo.compute_commission_for_one_user(user, self.date_range_id, rules)
+ if com_result:
+ com_result_ids.append(com_result.id)
+ else:
+ logger.debug(
+ "Commission computation: salesman '%s' "
+ "doesn't have a commission profile",
+ user.name)
+ return com_result_ids
+
+
diff --git a/commission_simple/wizard/commission_compute_view.xml b/commission_simple/wizard/commission_compute_view.xml
new file mode 100644
index 0000000..8ecac26
--- /dev/null
+++ b/commission_simple/wizard/commission_compute_view.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+ commission.compute.form
+ commission.compute
+
+
+
+
+
+
+ Compute Commissions
+ commission.compute
+ form
+ new
+
+
+
+
+
diff --git a/commission_simple_sale/__init__.py b/commission_simple_sale/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/commission_simple_sale/__manifest__.py b/commission_simple_sale/__manifest__.py
new file mode 100644
index 0000000..9aedc1c
--- /dev/null
+++ b/commission_simple_sale/__manifest__.py
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+# Copyright 2019 Akretion France (http://www.akretion.com)
+# @author Alexis de Lattre
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+{
+ 'name': 'Commission Simple Sale',
+ 'version': '10.0.1.0.0',
+ 'category': 'Sales',
+ 'license': 'AGPL-3',
+ 'summary': 'Give access to commission results to Salesman',
+ 'description': """
+Commission Simple Sale
+======================
+
+This module allows salesman to see their commissions in Odoo, under the Sales menu.
+
+This module has been written by Alexis de Lattre from Akretion
+.
+ """,
+ 'author': 'Akretion',
+ 'website': 'http://www.akretion.com',
+ 'depends': [
+ 'sale',
+ 'commission_simple',
+ ],
+ 'data': [
+ 'views/commission.xml',
+ 'security/rule.xml',
+ 'security/ir.model.access.csv',
+ ],
+ 'installable': True,
+}
diff --git a/commission_simple_sale/security/ir.model.access.csv b/commission_simple_sale/security/ir.model.access.csv
new file mode 100644
index 0000000..e54abab
--- /dev/null
+++ b/commission_simple_sale/security/ir.model.access.csv
@@ -0,0 +1,2 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_commission_result_salesman_read,Read access on commission.result to salesman,commission_simple.model_commission_result,sales_team.group_sale_salesman,1,0,0,0
diff --git a/commission_simple_sale/security/rule.xml b/commission_simple_sale/security/rule.xml
new file mode 100644
index 0000000..00e8e15
--- /dev/null
+++ b/commission_simple_sale/security/rule.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+ Commission Result for Salesman
+
+
+ [('user_id', '=', user.id)]
+
+
+
+
diff --git a/commission_simple_sale/views/commission.xml b/commission_simple_sale/views/commission.xml
new file mode 100644
index 0000000..b5c915d
--- /dev/null
+++ b/commission_simple_sale/views/commission.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+