diff --git a/addons/account/models/account.py b/addons/account/models/account.py
index 760bec54..da2896af 100644
--- a/addons/account/models/account.py
+++ b/addons/account/models/account.py
@@ -45,6 +45,7 @@ class AccountAccountTag(models.Model):
class AccountAccount(models.Model):
_name = "account.account"
_description = "Account"
+ _inherit = ['ir.branch.company.mixin']
_order = "code"
@api.multi
@@ -84,6 +85,17 @@ class AccountAccount(models.Model):
('code_company_uniq', 'unique (code,company_id)', 'The code of the account must be unique per company !')
]
+ @api.multi
+ @api.constrains('branch_id', 'company_id')
+ def _check_company_branch(self):
+ for record in self:
+ if record.branch_id and record.company_id != record.branch_id.company_id:
+ raise ValidationError(_(
+ 'Configuration Error of Company:\n'
+ 'The Account Company (%s) and the Company (%s) of '
+ 'Branch must be the same!') % (
+ record.company_id.name, record.branch_id.company_id.name))
+
def _compute_opening_debit_credit(self):
for record in self:
opening_debit = opening_credit = 0.0
@@ -305,6 +317,7 @@ class AccountGroup(models.Model):
class AccountJournal(models.Model):
_name = "account.journal"
+ _inherit = ['ir.branch.company.mixin']
_description = "Journal"
_order = 'sequence, type, code'
@@ -387,6 +400,17 @@ class AccountJournal(models.Model):
('code_company_uniq', 'unique (code, name, company_id)', 'The code and name of the journal must be unique per company !'),
]
+ @api.multi
+ @api.constrains('branch_id', 'company_id')
+ def _check_company_branch(self):
+ for record in self:
+ if record.branch_id and record.company_id != record.branch_id.company_id:
+ raise ValidationError(_(
+ 'Configuration Error of Company:\n'
+ 'The Account Company (%s) and the Company (%s) of '
+ 'Branch must be the same!') % (
+ record.company_id.name, record.branch_id.company_id.name))
+
@api.multi
# do not depend on 'sequence_id.date_range_ids', because
# sequence_id._get_current_sequence() may invalidate it!
@@ -720,6 +744,7 @@ class AccountTaxGroup(models.Model):
class AccountTax(models.Model):
_name = 'account.tax'
+ _inherit = ['ir.branch.company.mixin']
_description = 'Tax'
_order = 'sequence,id'
@@ -1008,6 +1033,7 @@ class AccountTax(models.Model):
class AccountReconcileModel(models.Model):
_name = "account.reconcile.model"
+ _inherit = ['ir.branch.company.mixin']
_description = "Preset to create journal entries during a invoices and payments matching"
name = fields.Char(string='Button Label', required=True)
diff --git a/addons/account/models/account_bank_statement.py b/addons/account/models/account_bank_statement.py
index a1219c5a..acd22989 100644
--- a/addons/account/models/account_bank_statement.py
+++ b/addons/account/models/account_bank_statement.py
@@ -144,6 +144,8 @@ class AccountBankStatement(models.Model):
journal_type = fields.Selection(related='journal_id.type', help="Technical field used for usability purposes")
company_id = fields.Many2one('res.company', related='journal_id.company_id', string='Company', store=True, readonly=True,
default=lambda self: self.env['res.company']._company_default_get('account.bank.statement'))
+ branch_id = fields.Many2one(related='journal_id.branch_id',
+ string='Branch', store=True, readonly=True)
total_entry_encoding = fields.Monetary('Transactions Subtotal', compute='_end_balance', store=True, help="Total of transaction lines.")
balance_end = fields.Monetary('Computed Balance', compute='_end_balance', store=True, help='Balance as calculated based on Opening Balance and transaction lines')
@@ -366,6 +368,8 @@ class AccountBankStatementLine(models.Model):
note = fields.Text(string='Notes')
sequence = fields.Integer(index=True, help="Gives the sequence order when displaying a list of bank statement lines.", default=1)
company_id = fields.Many2one('res.company', related='statement_id.company_id', string='Company', store=True, readonly=True)
+ branch_id = fields.Many2one(related='statement_id.branch_id',
+ string='Company', store=True, readonly=True)
journal_entry_ids = fields.One2many('account.move.line', 'statement_line_id', 'Journal Items', copy=False, readonly=True)
amount_currency = fields.Monetary(help="The amount expressed in an optional other currency if it is a multi-currency entry.")
currency_id = fields.Many2one('res.currency', string='Currency', help="The optional other currency if it is a multi-currency entry.")
diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py
index 0213a3ca..5da26b99 100644
--- a/addons/account/models/account_invoice.py
+++ b/addons/account/models/account_invoice.py
@@ -41,7 +41,8 @@ MAGIC_COLUMNS = ('id', 'create_uid', 'create_date', 'write_uid', 'write_date')
class AccountInvoice(models.Model):
_name = "account.invoice"
- _inherit = ['mail.thread', 'mail.activity.mixin', 'portal.mixin']
+ _inherit = ['mail.thread', 'mail.activity.mixin', 'portal.mixin',
+ 'ir.branch.company.mixin']
_description = "Invoice"
_order = "date_invoice desc, number desc, id desc"
@@ -383,6 +384,18 @@ class AccountInvoice(models.Model):
domain += [('journal_id', '=', self.journal_id.id), ('state', 'not in', ['draft', 'cancel'])]
return journal_sequence, domain
+ @api.constrains('company_id', 'branch_id')
+ def _check_company(self):
+ for order in self:
+ if order.branch_id and order.company_id != order.branch_id.company_id:
+ raise ValidationError(
+ _('Configuration Error of Company:\n'
+ 'The Invoice Company (%s) and '
+ 'the Company (%s) of Branch must '
+ 'be the same company!') % (order.company_id.name,
+ order.branch_id.company_id.name)
+ )
+
def _compute_portal_url(self):
super(AccountInvoice, self)._compute_portal_url()
for order in self:
@@ -1131,6 +1144,7 @@ class AccountInvoice(models.Model):
date = inv.date or inv.date_invoice
move_vals = {
'ref': inv.reference,
+ 'branch_id': inv.branch_id and inv.branch_id.id,
'line_ids': line,
'journal_id': journal.id,
'date': date,
@@ -1493,6 +1507,9 @@ class AccountInvoiceLine(models.Model):
analytic_tag_ids = fields.Many2many('account.analytic.tag', string='Analytic Tags')
company_id = fields.Many2one('res.company', string='Company',
related='invoice_id.company_id', store=True, readonly=True, related_sudo=False)
+ branch_id = fields.Many2one(string='Company',
+ related='invoice_id.branch_id', store=True,
+ readonly=True, related_sudo=False)
partner_id = fields.Many2one('res.partner', string='Partner',
related='invoice_id.partner_id', store=True, readonly=True, related_sudo=False)
currency_id = fields.Many2one('res.currency', related='invoice_id.currency_id', store=True, related_sudo=False)
@@ -1676,6 +1693,9 @@ class AccountInvoiceTax(models.Model):
manual = fields.Boolean(default=True)
sequence = fields.Integer(help="Gives the sequence order when displaying a list of invoice tax.")
company_id = fields.Many2one('res.company', string='Company', related='account_id.company_id', store=True, readonly=True)
+ branch_id = fields.Many2one(string='Branch',
+ related='account_id.branch_id', store=True,
+ readonly=True)
currency_id = fields.Many2one('res.currency', related='invoice_id.currency_id', store=True, readonly=True)
base = fields.Monetary(string='Base', compute='_compute_base_amount', store=True)
@@ -1687,6 +1707,7 @@ class AccountInvoiceTax(models.Model):
class AccountPaymentTerm(models.Model):
_name = "account.payment.term"
+ _inherit = ['ir.branch.company.mixin']
_description = "Payment Terms"
_order = "sequence, id"
diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py
index 18108c93..f2f618d6 100644
--- a/addons/account/models/account_move.py
+++ b/addons/account/models/account_move.py
@@ -19,6 +19,7 @@ class AccountMove(models.Model):
_name = "account.move"
_description = "Account Entry"
_order = 'date desc, id desc'
+ _inherit = ['ir.branch.company.mixin']
@api.multi
@api.depends('name', 'state')
@@ -445,6 +446,8 @@ class AccountMoveLine(models.Model):
analytic_account_id = fields.Many2one('account.analytic.account', string='Analytic Account')
analytic_tag_ids = fields.Many2many('account.analytic.tag', string='Analytic tags')
company_id = fields.Many2one('res.company', related='account_id.company_id', string='Company', store=True)
+ branch_id = fields.Many2one(related='move_id.branch_id', string='Branch',
+ store=True)
counterpart = fields.Char("Counterpart", compute='_get_counterpart', help="Compute the counter part accounts of this journal item for this journal entry. This can be needed in reports.")
# TODO: put the invoice link and partner_id on the account_move
@@ -463,6 +466,31 @@ class AccountMoveLine(models.Model):
('credit_debit2', 'CHECK (credit+debit>=0)', 'Wrong credit or debit value in accounting entry !'),
]
+ @api.constrains('move_id', 'branch_id')
+ def _check_branch(self):
+ for order in self:
+ move_branch_id = order.move_id.branch_id
+ if order.branch_id and move_branch_id != order.branch_id:
+ raise ValidationError(
+ _('Configuration Error of Branch:\n'
+ 'The Move Line Branch (%s) and '
+ 'the Branch (%s) of Journal Entry must '
+ 'be the same branch!') % (order.branch_id.name,
+ move_branch_id.name)
+ )
+
+ @api.constrains('company_id', 'branch_id')
+ def _check_company(self):
+ for order in self:
+ if order.branch_id and order.company_id != order.branch_id.company_id:
+ raise ValidationError(
+ _('Configuration Error of Company:\n'
+ 'The Move Line Company (%s) and '
+ 'the Company (%s) of Branch must '
+ 'be the same company!') % (order.company_id.name,
+ order.branch_id.company_id.name)
+ )
+
@api.model
def default_get(self, fields):
rec = super(AccountMoveLine, self).default_get(fields)
@@ -1446,6 +1474,7 @@ class AccountMoveLine(models.Model):
return {
'name': self.name,
'date': self.date,
+ 'branch_id': self.branch_id and self.branch_id.id,
'account_id': self.analytic_account_id.id,
'tag_ids': [(6, 0, self.analytic_tag_ids.ids)],
'unit_amount': self.quantity,
@@ -1538,6 +1567,8 @@ class AccountPartialReconcile(models.Model):
company_currency_id = fields.Many2one('res.currency', related='company_id.currency_id', readonly=True,
help='Utility field to express amount currency')
company_id = fields.Many2one('res.company', related='debit_move_id.company_id', store=True, string='Currency')
+ branch_id = fields.Many2one(related='debit_move_id.branch_id', store=True,
+ string='Branch')
full_reconcile_id = fields.Many2one('account.full.reconcile', string="Full Reconcile", copy=False)
max_date = fields.Date(string='Max Date of Matched Lines', compute='_compute_max_date',
readonly=True, copy=False, store=True,
diff --git a/addons/account/models/account_payment.py b/addons/account/models/account_payment.py
index e66f45a5..73ed3d4a 100644
--- a/addons/account/models/account_payment.py
+++ b/addons/account/models/account_payment.py
@@ -49,6 +49,8 @@ class account_abstract_payment(models.AbstractModel):
communication = fields.Char(string='Memo')
journal_id = fields.Many2one('account.journal', string='Payment Journal', required=True, domain=[('type', 'in', ('bank', 'cash'))])
company_id = fields.Many2one('res.company', related='journal_id.company_id', string='Company', readonly=True)
+ branch_id = fields.Many2one(related='journal_id.branch_id',
+ string='Branch', readonly=True)
hide_payment_method = fields.Boolean(compute='_compute_hide_payment_method',
help="Technical field used to hide the payment method if the selected journal has only one available which is 'manual'")
@@ -238,7 +240,7 @@ class account_register_payments(models.TransientModel):
class account_payment(models.Model):
_name = "account.payment"
- _inherit = ['mail.thread', 'account.abstract.payment']
+ _inherit = ['mail.thread', 'account.abstract.payment', 'ir.branch.company.mixin']
_description = "Payments"
_order = "payment_date desc, name desc"
diff --git a/addons/account/models/chart_template.py b/addons/account/models/chart_template.py
index 888c0d2f..f1e3bc07 100644
--- a/addons/account/models/chart_template.py
+++ b/addons/account/models/chart_template.py
@@ -57,6 +57,7 @@ def preserve_existing_tags_on_taxes(cr, registry, module):
class AccountAccountTemplate(models.Model):
_name = "account.account.template"
_description = 'Templates for Accounts'
+ _inherit = ['ir.branch.company.mixin']
_order = "code"
name = fields.Char(required=True, index=True)
@@ -498,6 +499,7 @@ class AccountChartTemplate(models.Model):
class AccountTaxTemplate(models.Model):
_name = 'account.tax.template'
+ _inherit = ['ir.branch.company.mixin']
_description = 'Templates for Taxes'
_order = 'id'
@@ -621,6 +623,7 @@ class AccountTaxTemplate(models.Model):
class AccountFiscalPositionTemplate(models.Model):
_name = 'account.fiscal.position.template'
+ _inherit = ['ir.branch.company.mixin']
_description = 'Template for Fiscal Position'
sequence = fields.Integer()
@@ -642,6 +645,7 @@ class AccountFiscalPositionTemplate(models.Model):
class AccountFiscalPositionTaxTemplate(models.Model):
_name = 'account.fiscal.position.tax.template'
+ _inherit = ['ir.branch.company.mixin']
_description = 'Template Tax Fiscal Position'
_rec_name = 'position_id'
@@ -653,6 +657,7 @@ class AccountFiscalPositionTaxTemplate(models.Model):
class AccountFiscalPositionAccountTemplate(models.Model):
_name = 'account.fiscal.position.account.template'
_description = 'Template Account Fiscal Mapping'
+ _inherit = ['ir.branch.company.mixin']
_rec_name = 'position_id'
position_id = fields.Many2one('account.fiscal.position.template', string='Fiscal Mapping', required=True, ondelete='cascade')
@@ -961,6 +966,7 @@ class AccountBankAccountsWizard(models.TransientModel):
class AccountReconcileModelTemplate(models.Model):
_name = "account.reconcile.model.template"
+ _inherit = ['ir.branch.company.mixin']
chart_template_id = fields.Many2one('account.chart.template', string='Chart Template', required=True)
name = fields.Char(string='Button Label', required=True)
diff --git a/addons/account/models/partner.py b/addons/account/models/partner.py
index b8108fc9..e234bf7e 100644
--- a/addons/account/models/partner.py
+++ b/addons/account/models/partner.py
@@ -11,6 +11,7 @@ from flectra.addons.base.res.res_partner import WARNING_MESSAGE, WARNING_HELP
class AccountFiscalPosition(models.Model):
_name = 'account.fiscal.position'
+ _inherit = ['ir.branch.company.mixin']
_description = 'Fiscal Position'
_order = 'sequence'
@@ -164,6 +165,7 @@ class AccountFiscalPosition(models.Model):
class AccountFiscalPositionTax(models.Model):
_name = 'account.fiscal.position.tax'
+ _inherit = ['ir.branch.company.mixin']
_description = 'Taxes Fiscal Position'
_rec_name = 'position_id'
@@ -182,6 +184,7 @@ class AccountFiscalPositionTax(models.Model):
class AccountFiscalPositionAccount(models.Model):
_name = 'account.fiscal.position.account'
_description = 'Accounts Fiscal Position'
+ _inherit = ['ir.branch.company.mixin']
_rec_name = 'position_id'
position_id = fields.Many2one('account.fiscal.position', string='Fiscal Position',
diff --git a/addons/account/report/account_aged_partner_balance.py b/addons/account/report/account_aged_partner_balance.py
index c85bd70c..8353516b 100644
--- a/addons/account/report/account_aged_partner_balance.py
+++ b/addons/account/report/account_aged_partner_balance.py
@@ -29,6 +29,10 @@ class ReportAgedPartnerBalance(models.AbstractModel):
cr = self.env.cr
user_company = self.env.user.company_id.id
move_state = ['draft', 'posted']
+ branch_id = data['form'].get('branch_id', False)
+ branch = ''
+ if branch_id:
+ branch = 'AND (l.branch_id =' + str(branch_id[0]) + ')'
if target_move == 'posted':
move_state = ['posted']
arg_list = (tuple(move_state), tuple(account_type))
@@ -49,7 +53,7 @@ class ReportAgedPartnerBalance(models.AbstractModel):
AND (l.move_id = am.id)
AND (am.state IN %s)
AND (account_account.internal_type IN %s)
- AND ''' + reconciliation_clause + '''
+ AND ''' + reconciliation_clause + branch +'''
AND (l.date <= %s)
AND l.company_id = %s
ORDER BY UPPER(res_partner.name)'''
@@ -75,7 +79,7 @@ class ReportAgedPartnerBalance(models.AbstractModel):
AND (account_account.internal_type IN %s)
AND (COALESCE(l.date_maturity,l.date) > %s)\
AND ((l.partner_id IN %s) OR (l.partner_id IS NULL))
- AND (l.date <= %s)
+ AND (l.date <= %s) ''' + branch + '''
AND l.company_id = %s'''
cr.execute(query, (tuple(move_state), tuple(account_type), date_from, tuple(partner_ids), date_from, user_company))
aml_ids = cr.fetchall()
@@ -125,7 +129,7 @@ class ReportAgedPartnerBalance(models.AbstractModel):
AND (am.state IN %s)
AND (account_account.internal_type IN %s)
AND ((l.partner_id IN %s) OR (l.partner_id IS NULL))
- AND ''' + dates_query + '''
+ AND ''' + dates_query + branch +'''
AND (l.date <= %s)
AND l.company_id = %s'''
cr.execute(query, args_list)
diff --git a/addons/account/report/account_invoice_report.py b/addons/account/report/account_invoice_report.py
index 9799ba80..d70d9346 100644
--- a/addons/account/report/account_invoice_report.py
+++ b/addons/account/report/account_invoice_report.py
@@ -6,6 +6,7 @@ from flectra import models, fields, api
class AccountInvoiceReport(models.Model):
_name = "account.invoice.report"
+ _inherit = ['ir.branch.company.mixin']
_description = "Invoices Statistics"
_auto = False
_rec_name = 'date'
@@ -72,7 +73,7 @@ class AccountInvoiceReport(models.Model):
_depends = {
'account.invoice': [
- 'account_id', 'amount_total_company_signed', 'commercial_partner_id', 'company_id',
+ 'account_id', 'amount_total_company_signed', 'commercial_partner_id', 'company_id', 'branch_id',
'currency_id', 'date_due', 'date_invoice', 'fiscal_position_id',
'journal_id', 'partner_bank_id', 'partner_id', 'payment_term_id',
'residual', 'state', 'type', 'user_id',
@@ -92,7 +93,7 @@ class AccountInvoiceReport(models.Model):
select_str = """
SELECT sub.id, sub.date, sub.product_id, sub.partner_id, sub.country_id, sub.account_analytic_id,
sub.payment_term_id, sub.uom_name, sub.currency_id, sub.journal_id,
- sub.fiscal_position_id, sub.user_id, sub.company_id, sub.nbr, sub.type, sub.state,
+ sub.fiscal_position_id, sub.user_id, sub.company_id, sub.branch_id, sub.nbr, sub.type, sub.state,
sub.categ_id, sub.date_due, sub.account_id, sub.account_line_id, sub.partner_bank_id,
sub.product_qty, sub.price_total as price_total, sub.price_average as price_average,
COALESCE(cr.rate, 1) as currency_rate, sub.residual as residual, sub.commercial_partner_id as commercial_partner_id
@@ -105,7 +106,7 @@ class AccountInvoiceReport(models.Model):
ai.date_invoice AS date,
ail.product_id, ai.partner_id, ai.payment_term_id, ail.account_analytic_id,
u2.name AS uom_name,
- ai.currency_id, ai.journal_id, ai.fiscal_position_id, ai.user_id, ai.company_id,
+ ai.currency_id, ai.journal_id, ai.fiscal_position_id, ai.user_id, ai.company_id, ai.branch_id,
1 AS nbr,
ai.type, ai.state, pt.categ_id, ai.date_due, ai.account_id, ail.account_id AS account_line_id,
ai.partner_bank_id,
@@ -148,7 +149,7 @@ class AccountInvoiceReport(models.Model):
group_by_str = """
GROUP BY ail.id, ail.product_id, ail.account_analytic_id, ai.date_invoice, ai.id,
ai.partner_id, ai.payment_term_id, u2.name, u2.id, ai.currency_id, ai.journal_id,
- ai.fiscal_position_id, ai.user_id, ai.company_id, ai.type, invoice_type.sign, ai.state, pt.categ_id,
+ ai.fiscal_position_id, ai.user_id, ai.company_id, ai.branch_id, ai.type, invoice_type.sign, ai.state, pt.categ_id,
ai.date_due, ai.account_id, ail.account_id, ai.partner_bank_id, ai.residual_company_signed,
ai.amount_total_company_signed, ai.commercial_partner_id, partner.country_id
"""
diff --git a/addons/account/security/account_security.xml b/addons/account/security/account_security.xml
index abacf2db..89009e9d 100644
--- a/addons/account/security/account_security.xml
+++ b/addons/account/security/account_security.xml
@@ -44,6 +44,73 @@
+
+
+
+ Account Entry Multi Branch
+
+
+ ['|',('branch_id','=', False),'|',('branch_id','=',user.default_branch_id.id), ('branch_id','in', [b.id for b in user.branch_ids])]
+
+
+
+ Entry lines Multi Branch
+
+
+ ['|',('branch_id','=', False),'|',('branch_id','=',user.default_branch_id.id), ('branch_id','in', [b.id for b in user.branch_ids])]
+
+
+
+ Invoice multi-branch
+
+
+ ['|',('branch_id','=', False),'|',('branch_id','=',user.default_branch_id.id), ('branch_id','in', [b.id for b in user.branch_ids])]
+
+
+
+ Invoice Analysis multi-branch
+
+
+ ['|',('branch_id','=', False),'|',('branch_id','=',user.default_branch_id.id), ('branch_id','in', [b.id for b in user.branch_ids])]
+
+
+
+ Invoice Line branch rule
+
+
+ ['|',('branch_id','=', False),'|',('branch_id','=',user.default_branch_id.id), ('branch_id','in', [b.id for b in user.branch_ids])]
+
+
+
+ Account bank statement branch rule
+
+
+ ['|',('branch_id','=', False),'|',('branch_id','=',user.default_branch_id.id), ('branch_id','in', [b.id for b in user.branch_ids])]
+
+
+
+ Account bank statement line branch rule
+
+
+ ['|',('branch_id','=', False),'|',('branch_id','=',user.default_branch_id.id), ('branch_id','in', [b.id for b in user.branch_ids])]
+
+
+
+ Account reconcile model template company rule
+
+
+ ['|',('branch_id','=', False),'|',('branch_id','=',user.default_branch_id.id), ('branch_id','in', [b.id for b in user.branch_ids])]
+
+
+
+ Account payment company rule
+
+
+ ['|',('branch_id','=', False),'|',('branch_id','=',user.default_branch_id.id), ('branch_id','in', [b.id for b in user.branch_ids])]
+
+
+
+
Account Entry
diff --git a/addons/account/tests/__init__.py b/addons/account/tests/__init__.py
index 8d6fb294..71f06a2c 100644
--- a/addons/account/tests/__init__.py
+++ b/addons/account/tests/__init__.py
@@ -18,3 +18,8 @@ from . import test_search
from . import test_setup_bar
from . import test_tax
from . import test_templates_consistency
+from . import test_account_branch
+from . import test_invoice_branch
+from . import test_journal_entries_branch
+from . import test_branch_moves
+from . import test_payment_branch
diff --git a/addons/account/tests/test_account_branch.py b/addons/account/tests/test_account_branch.py
new file mode 100644
index 00000000..cccbd696
--- /dev/null
+++ b/addons/account/tests/test_account_branch.py
@@ -0,0 +1,91 @@
+# -*- coding: utf-8 -*-
+from flectra.addons.account.tests.account_test_classes import AccountingTestCase
+
+
+class TestAccountBranch(AccountingTestCase):
+ def setUp(self):
+ super(TestAccountBranch, self).setUp()
+ self.apple_product = self.env.ref('product.product_product_7')
+ self.keyboard_product = self.env.ref('product.product_product_9')
+ self.ipod_product = self.env.ref('product.product_product_11')
+ self.asset_account = self.env.ref('l10n_generic_coa.conf_stk')
+ self.model_account_journal = self.env['account.journal']
+ self.model_account = self.env['account.account']
+ self.main_company = self.env.ref('base.main_company')
+ self.manager_group = self.env.ref('account.group_account_manager')
+ self.model_user = self.env['res.users']
+ self.model_account_invoice = self.env['account.invoice']
+ self.account_partner = self.env.ref('base.res_partner_1')
+ self.branch_1 = self.env.ref('base_branch_company.data_branch_1')
+ self.branch_2 = self.env.ref('base_branch_company.data_branch_2')
+ self.branch_3 = self.env.ref('base_branch_company.data_branch_3')
+ user_type = self.env.ref('account.data_account_type_liquidity')
+ self.account_type = self.env.ref('account.data_account_type_expenses')
+
+ self.user_id = self.model_user.with_context(
+ {'no_reset_password': True}).create({
+ 'company_id': self.main_company.id,
+ 'branch_ids': [(4, self.branch_2.id), (4, self.branch_3.id)],
+ 'company_ids': [(4, self.main_company.id)],
+ 'groups_id': [(6, 0, [self.manager_group.id])],
+ 'name': 'Test User 1',
+ 'email': 'demo@yourcompany.com',
+ 'password': '123',
+ 'login': 'tes_user_1',
+ })
+
+ self.user_2 = self.model_user.with_context({
+ 'no_reset_password': True}).create({
+ 'company_id': self.main_company.id,
+ 'branch_ids': [(4, self.branch_3.id)],
+ 'company_ids': [(4, self.main_company.id)],
+ 'groups_id': [(6, 0, [self.manager_group.id])],
+ 'name': 'Test User',
+ 'email': 'demo@yourcompany.com',
+ 'password': '123',
+ 'login': 'test_user_2',
+ })
+
+ self.cash_account = self.model_account.create({
+ 'company_id': self.main_company.id,
+ 'user_type_id': user_type.id,
+ 'code': 'cash_test',
+ 'name': 'Test Cash Account',
+ })
+
+ self.cash_journal = self.model_account_journal.create({
+ 'company_id': self.main_company.id,
+ 'branch_id': self.branch_1.id,
+ 'name': 'Cash Journal - Branch 1',
+ 'default_credit_account_id': self.cash_account.id,
+ 'default_debit_account_id': self.cash_account.id,
+ 'type': 'cash',
+ 'code': 'cash_branch_1',
+ })
+
+ def invoice_values(self, branch_id):
+ products = [(self.apple_product, 1000),
+ (self.keyboard_product, 500),
+ (self.ipod_product, 800)]
+ lines_data = []
+ account_id = self.model_account.search([
+ ('user_type_id', '=', self.account_type.id)], limit=1).id
+ for product_id, quantity in products:
+ values = {
+ 'product_id': product_id.id,
+ 'name': product_id.name,
+ 'price_unit': 120,
+ 'quantity': quantity,
+ 'account_id': account_id
+ }
+ lines_data.append((0, 0, values))
+ vals = {
+ 'partner_id': self.account_partner.id,
+ 'type': 'in_invoice',
+ 'name': "Supplier Invoice",
+ # 'reference_type': "none",
+ 'account_id': self.account_partner.property_account_payable_id.id,
+ 'invoice_line_ids': lines_data,
+ 'branch_id': branch_id,
+ }
+ return vals
diff --git a/addons/account/tests/test_branch_moves.py b/addons/account/tests/test_branch_moves.py
new file mode 100644
index 00000000..e8b22c1d
--- /dev/null
+++ b/addons/account/tests/test_branch_moves.py
@@ -0,0 +1,11 @@
+# -*- coding: utf-8 -*-
+from . import test_account_branch
+
+
+class TestBranchJournalEntries(test_account_branch.TestAccountBranch):
+
+ def test_branch_security_move_line(self):
+ move_ids = self.env['account.move.line'].sudo(self.user_2.id).\
+ search([('branch_id', '=', self.branch_2.id)])
+ self.assertFalse(move_ids, 'USer 2 should not have access to move lines with Branch %s'
+ % self.branch_2.name)
diff --git a/addons/account/tests/test_invoice_branch.py b/addons/account/tests/test_invoice_branch.py
new file mode 100644
index 00000000..6febaa8b
--- /dev/null
+++ b/addons/account/tests/test_invoice_branch.py
@@ -0,0 +1,18 @@
+# -*- coding: utf-8 -*-
+from . import test_account_branch
+
+
+class TestInvoiceBranch(test_account_branch.TestAccountBranch):
+
+ def test_invoice_create(self):
+ self.invoice_id = self.model_account_invoice.sudo(self.user_id.id).create(self.invoice_values(self.branch_2.id))
+
+ invoices = self.model_account_invoice.sudo(self.user_2.id).search([('branch_id', '=', self.branch_2.id)])
+
+ self.assertFalse(invoices, 'USer 2 should not have access to Invoice with Branch %s'
+ % self.branch_2.name)
+
+ self.invoice_id.sudo(self.user_id.id).action_invoice_open()
+ all_branch = all(move_line_id.branch_id.id == self.branch_2.id for
+ move_line_id in self.invoice_id.move_id.line_ids)
+ self.assertNotEqual(all_branch, False, 'Journal Entries have different Branch.')
diff --git a/addons/account/tests/test_journal_entries_branch.py b/addons/account/tests/test_journal_entries_branch.py
new file mode 100644
index 00000000..f73de1e1
--- /dev/null
+++ b/addons/account/tests/test_journal_entries_branch.py
@@ -0,0 +1,63 @@
+# -*- coding: utf-8 -*-
+from . import test_account_branch
+
+
+class TestJournalEntryBranch(test_account_branch.TestAccountBranch):
+
+ def test_journal_entries_branch(self):
+ journal_ids = self.model_account_journal.search([('code', '=', 'MISC')],
+ limit=1)
+ move_vals = self.env['account.move'].default_get([])
+ lines = [
+ (0, 0, {
+ 'name': 'Test',
+ 'account_id': self.asset_account.id,
+ 'debit': 0,
+ 'credit': 100,
+ 'branch_id': self.branch_1.id,
+ }),
+ (0, 0, {
+ 'name': 'Test',
+ 'account_id': self.asset_account.id,
+ 'debit': 100,
+ 'credit': 0,
+ 'branch_id': self.branch_1.id,
+ })
+ ]
+ move_vals.update({
+ 'journal_id': journal_ids and journal_ids.id,
+ 'line_ids': lines,
+ })
+ move = self.env['account.move'].sudo(self.user_id.id).create(move_vals)
+ move.post()
+
+ def _check_balance(self, account_id, acc_type='clearing'):
+ domain = [('account_id', '=', account_id)]
+ balance = self._get_balance(domain)
+ self.assertEqual(balance, 0.0, 'Balance is 0 for all Branch.')
+ domain = [('account_id', '=', account_id),
+ ('branch_id', '=', self.branch_2.id)]
+ balance = self._get_balance(domain)
+ if acc_type == 'other':
+ self.assertEqual(balance, -100,
+ 'Balance is -100 for Branch.')
+ else:
+ self.assertEqual(balance, 100,
+ 'Balance is 100 for Branch.')
+ domain = [('account_id', '=', account_id),
+ ('branch_id', '=', self.branch_3.id)]
+ balance = self._get_balance(domain)
+ if acc_type == 'other':
+ self.assertEqual(balance, 100.0,
+ 'Balance is 100 for Branch')
+ else:
+ self.assertEqual(balance, -100.0,
+ 'Balance is -100 for Branch')
+
+ def _get_balance(self, domain):
+
+ aml_rec = self.env['account.move.line'].sudo(self.user_id.id).read_group(domain,['debit', 'credit', 'account_id'], ['account_id'])
+ if aml_rec:
+ aml_rec = aml_rec[0]
+ a = aml_rec.get('debit', 0) - aml_rec.get('credit', 0)
+ return a
diff --git a/addons/account/tests/test_payment_branch.py b/addons/account/tests/test_payment_branch.py
new file mode 100644
index 00000000..9135bae7
--- /dev/null
+++ b/addons/account/tests/test_payment_branch.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+from flectra.addons.account.tests import test_account_branch
+import time
+
+
+class TestPaymentsBranch(test_account_branch.TestAccountBranch):
+
+ def test_payment_branch(self):
+ self.invoice_id = self.model_account_invoice.sudo(self.user_id.id).create(
+ self.invoice_values(self.branch_2.id))
+ self.invoice_id.sudo(self.user_id.id).action_invoice_open()
+
+ context = {'active_ids': [self.invoice_id.id], 'active_model': 'account.invoice'}
+ create_payments = self.env['account.register.payments'].sudo(self.user_id.id).with_context(context).create({
+ 'payment_method_id': self.env.ref("account.account_payment_method_manual_in").id,
+ 'journal_id': self.cash_journal.id,
+ 'payment_date': time.strftime('%Y') + '-12-17',
+
+ })
+ create_payments.create_payments()
+ payment = self.env['account.payment'].sudo(self.user_2.id).search([('branch_id', '=', self.branch_2.id)])
+ self.assertFalse(payment, 'USer 2 should not have access to Payments with Branch %s'
+ % self.branch_2.name)
diff --git a/addons/account/views/account_invoice_view.xml b/addons/account/views/account_invoice_view.xml
index cbe8e603..8c246d08 100644
--- a/addons/account/views/account_invoice_view.xml
+++ b/addons/account/views/account_invoice_view.xml
@@ -330,6 +330,7 @@
+
@@ -477,6 +478,7 @@
+
diff --git a/addons/account/views/account_view.xml b/addons/account/views/account_view.xml
index 535cbb56..e6348f23 100644
--- a/addons/account/views/account_view.xml
+++ b/addons/account/views/account_view.xml
@@ -1155,6 +1155,7 @@
+
@@ -1227,6 +1228,7 @@
+
@@ -1304,6 +1306,7 @@
+
@@ -1371,6 +1374,7 @@
+
@@ -1452,6 +1456,7 @@
+
@@ -1465,6 +1470,7 @@
+
diff --git a/addons/account/views/report_generalledger.xml b/addons/account/views/report_generalledger.xml
index 81566721..c612aec5 100644
--- a/addons/account/views/report_generalledger.xml
+++ b/addons/account/views/report_generalledger.xml
@@ -40,6 +40,10 @@
Date from :
Date to :
+
diff --git a/addons/account/views/report_invoice.xml b/addons/account/views/report_invoice.xml
index e4d809fb..c7178ddd 100644
--- a/addons/account/views/report_invoice.xml
+++ b/addons/account/views/report_invoice.xml
@@ -48,6 +48,10 @@
Reference:
+
diff --git a/addons/account/views/report_partnerledger.xml b/addons/account/views/report_partnerledger.xml
index 782fc44d..5482d71f 100644
--- a/addons/account/views/report_partnerledger.xml
+++ b/addons/account/views/report_partnerledger.xml
@@ -24,6 +24,10 @@
All Entries
All Posted Entries
+
diff --git a/addons/account/wizard/account_report_aged_partner_balance_view.xml b/addons/account/wizard/account_report_aged_partner_balance_view.xml
index 6791dc45..c1653124 100644
--- a/addons/account/wizard/account_report_aged_partner_balance_view.xml
+++ b/addons/account/wizard/account_report_aged_partner_balance_view.xml
@@ -12,6 +12,8 @@
+
+
diff --git a/addons/account/wizard/account_report_common.py b/addons/account/wizard/account_report_common.py
index 9712be6b..a74cb513 100644
--- a/addons/account/wizard/account_report_common.py
+++ b/addons/account/wizard/account_report_common.py
@@ -6,6 +6,7 @@ from flectra import api, fields, models, _
class AccountCommonReport(models.TransientModel):
_name = "account.common.report"
_description = "Account Common Report"
+ _inherit = ['ir.branch.company.mixin']
company_id = fields.Many2one('res.company', string='Company', readonly=True, default=lambda self: self.env.user.company_id)
journal_ids = fields.Many2many('account.journal', string='Journals', required=True, default=lambda self: self.env['account.journal'].search([]))
@@ -21,6 +22,7 @@ class AccountCommonReport(models.TransientModel):
result['state'] = 'target_move' in data['form'] and data['form']['target_move'] or ''
result['date_from'] = data['form']['date_from'] or False
result['date_to'] = data['form']['date_to'] or False
+ result['branch_id'] = data['form']['branch_id'] or False
result['strict_range'] = True if result['date_from'] else False
return result
@@ -33,7 +35,7 @@ class AccountCommonReport(models.TransientModel):
data = {}
data['ids'] = self.env.context.get('active_ids', [])
data['model'] = self.env.context.get('active_model', 'ir.ui.menu')
- data['form'] = self.read(['date_from', 'date_to', 'journal_ids', 'target_move'])[0]
+ data['form'] = self.read(['date_from', 'date_to', 'journal_ids', 'target_move', 'branch_id'])[0]
used_context = self._build_contexts(data)
data['form']['used_context'] = dict(used_context, lang=self.env.context.get('lang') or 'en_US')
return self._print_report(data)
diff --git a/addons/account/wizard/account_report_common_view.xml b/addons/account/wizard/account_report_common_view.xml
index 5c8c9fdd..13b137a3 100644
--- a/addons/account/wizard/account_report_common_view.xml
+++ b/addons/account/wizard/account_report_common_view.xml
@@ -12,6 +12,12 @@
+
+
+
+
+
+
diff --git a/addons/account_voucher/models/account_voucher.py b/addons/account_voucher/models/account_voucher.py
index 09a1dae9..67c7294c 100644
--- a/addons/account_voucher/models/account_voucher.py
+++ b/addons/account_voucher/models/account_voucher.py
@@ -4,7 +4,7 @@
from flectra import fields, models, api, _
from flectra.addons import decimal_precision as dp
-from flectra.exceptions import UserError
+from flectra.exceptions import UserError, ValidationError
class AccountVoucher(models.Model):
@@ -46,7 +46,7 @@ class AccountVoucher(models.Model):
narration = fields.Text('Notes', readonly=True, states={'draft': [('readonly', False)]})
currency_id = fields.Many2one('res.currency', compute='_get_journal_currency',
string='Currency', readonly=True, required=True, default=lambda self: self._get_currency())
- company_id = fields.Many2one('res.company', 'Company',
+ company_id = fields.Many2one('res.company', 'Company', store=True,
required=True, readonly=True, states={'draft': [('readonly', False)]},
related='journal_id.company_id', default=lambda self: self._get_company())
state = fields.Selection([
@@ -74,6 +74,20 @@ class AccountVoucher(models.Model):
('pay_later', 'Pay Later'),
], 'Payment', index=True, readonly=True, states={'draft': [('readonly', False)]}, default='pay_later')
date_due = fields.Date('Due Date', readonly=True, index=True, states={'draft': [('readonly', False)]})
+ branch_id = fields.Many2one('res.branch', 'Branch', ondelete="restrict",
+ default=lambda self: self.env['res.users']._get_default_branch())
+
+ @api.constrains('company_id', 'branch_id')
+ def _check_company_branch(self):
+ for record in self:
+ if record.branch_id and record.company_id != record.branch_id.company_id:
+ raise ValidationError(
+ _('Configuration Error of Company:\n'
+ 'The Company (%s) in the voucher and '
+ 'the Company (%s) of Branch must '
+ 'be the same company!') % (record.company_id.name,
+ record.branch_id.company_id.name)
+ )
@api.one
@api.depends('move_id.line_ids.reconciled', 'move_id.line_ids.account_id.internal_type')
@@ -186,6 +200,7 @@ class AccountVoucher(models.Model):
'date': self.account_date,
'date_maturity': self.date_due,
'payment_id': self._context.get('payment_id'),
+ 'branch_id': self.branch_id.id,
}
return move_line
@@ -199,13 +214,13 @@ class AccountVoucher(models.Model):
name = self.journal_id.sequence_id.with_context(ir_sequence_date=self.date).next_by_id()
else:
raise UserError(_('Please define a sequence on the journal.'))
-
move = {
'name': name,
'journal_id': self.journal_id.id,
'narration': self.narration,
'date': self.account_date,
'ref': self.reference,
+ 'branch_id': self.branch_id.id
}
return move
@@ -361,6 +376,10 @@ class AccountVoucherLine(models.Model):
company_id = fields.Many2one('res.company', related='voucher_id.company_id', string='Company', store=True, readonly=True)
tax_ids = fields.Many2many('account.tax', string='Tax', help="Only for tax excluded from price")
currency_id = fields.Many2one('res.currency', related='voucher_id.currency_id')
+ branch_id = fields.Many2one('res.branch',
+ related='voucher_id.branch_id',
+ string='Branch',
+ readonly=True, store=True)
@api.one
@api.depends('price_unit', 'tax_ids', 'quantity', 'product_id', 'voucher_id.currency_id')
diff --git a/addons/account_voucher/security/account_voucher_security.xml b/addons/account_voucher/security/account_voucher_security.xml
index 5c9863de..f8818090 100644
--- a/addons/account_voucher/security/account_voucher_security.xml
+++ b/addons/account_voucher/security/account_voucher_security.xml
@@ -13,5 +13,20 @@
['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]
+
+
+ ['|',('branch_id','=', False),'|',('branch_id','=',user.default_branch_id.id), ('branch_id','in', [b.id for b in user.branch_ids])]
+ Vouchers Branch
+
+
+
+
+ ['|',('branch_id','=', False),'|',('branch_id','=',user.default_branch_id.id), ('branch_id','in', [b.id for b in user.branch_ids])]
+ Voucher lines branch
+
+
diff --git a/addons/account_voucher/tests/__init__.py b/addons/account_voucher/tests/__init__.py
new file mode 100644
index 00000000..2f9e20f7
--- /dev/null
+++ b/addons/account_voucher/tests/__init__.py
@@ -0,0 +1,4 @@
+# -*- coding: utf-8 -*-
+
+from . import test_account_voucher_branch
+from . import test_voucher_receipt_branch
diff --git a/addons/account_voucher/tests/test_account_voucher_branch.py b/addons/account_voucher/tests/test_account_voucher_branch.py
new file mode 100644
index 00000000..6f9b4ed0
--- /dev/null
+++ b/addons/account_voucher/tests/test_account_voucher_branch.py
@@ -0,0 +1,103 @@
+# -*- coding: utf-8 -*-
+
+from datetime import date
+from flectra.tests import common
+from flectra.api import Environment
+
+
+
+class TestAccountVoucherBranch(common.TransactionCase):
+
+ def setUp(self):
+ super(TestAccountVoucherBranch, self).setUp()
+ self.account_user = self.env.ref('account.group_account_manager')
+ self.model_account = self.env['account.account']
+ self.main_company = self.env.ref('base.main_company')
+ self.model_journal = self.env['account.journal']
+ self.partner = self.env.ref('base.res_partner_1')
+ self.model_voucher = self.env['account.voucher']
+ self.apple_product = self.env.ref('product.product_product_7')
+ self.model_voucher_line = self.env['account.voucher.line']
+ self.branch_1 = self.env.ref('base_branch_company.data_branch_1')
+ self.model_users = self.env['res.users']
+ self.branch_2 = self.env.ref('base_branch_company.data_branch_2')
+ self.income_type = self.env['account.account.type'].search([('name', '=', 'Income')])
+ account_obj = self.env.ref['account.account']
+ self.account_receivable = account_obj.create(
+ {'code': 'X1012', 'name': 'Account Receivable - Test',
+ 'user_type_id': self.env.ref('account.data_account_type_receivable').id,
+ 'reconcile': True})
+ self.account_1 = self.account_create('acc_code_1', self.income_type.id, self.main_company.id)
+ self.account_2 = self.account_create('acc_code_2', self.income_type.id, self.main_company.id)
+ self.journal_1 = self.journal_create('journal_code_1', self.account_1, self.main_company.id)
+ self.journal_2 = self.journal_create('journal_code_2', self.account_2, self.main_company.id)
+ self.branch_user_1 = self.user_create(
+ 'branch_user_1', self.branch_1, self.main_company,
+ [self.branch_1, self.branch_2], [self.account_user])
+ self.branch_user_2 = self.user_create('branch_user_2', self.branch_2, self.main_company, [self.branch_2],
+ [self.account_user])
+
+ self.account_voucher_1 = self.receipt_create(self.journal_1, self.branch_1)
+ self.account_voucher_2 = self.receipt_create(self.journal_2, self.branch_2)
+
+ def account_create(self, code, type, company_id):
+ data = {'code': code,
+ 'name': 'Test Sales Account ' + code,
+ 'company_id': company_id,
+ 'user_type_id': type,
+ }
+ account_obj = self.model_account.create(data)
+ return account_obj.id
+
+ def journal_create(self, code, account_id, company_id):
+ data ={
+ 'code': code,
+ 'name': 'Test Sales Account ' + code,
+ 'type': 'sale',
+ 'default_debit_account_id': account_id,
+ 'default_credit_account_id': account_id,
+ 'company_id': company_id
+ }
+ journal_obj = self.model_journal.create(data)
+ return journal_obj.id
+
+ def user_create(self, user_name, branch_id, company_id, branch_ids, groups_ids ):
+ group_ids = [group.id for group in groups_ids]
+ data = {
+ 'login': user_name,
+ 'name': 'Test User ' + user_name,
+ 'email': 'demo@yourcompany.com',
+ 'branch_id':branch_id.id,
+ 'password': 'test@123',
+ 'company_id': company_id.id,
+ 'groups_id': [(6, 0, group_ids)],
+ 'company_ids': [(4, company_id.id)],
+ 'branch_ids': [(4, branch.id) for branch in branch_ids],
+ }
+ user_obj = self.model_users.with_context({'no_reset_password': True}).create(data)
+ return user_obj.id
+
+ def receipt_create(self, journal_id, branch_id):
+ print ("@@@@@@@@@@@@@@@@",self.receivable_account )
+ vals = {
+ 'name': 'Test Voucher',
+ 'partner_id': self.partner.id,
+ 'journal_id': journal_id,
+ 'voucher_type': 'sale',
+ 'account_id': self.receivable_account.id,
+ 'branch_id': branch_id.id,
+ 'company_id': self.main_company.id,
+ 'date': date.today(),
+ }
+ voucher_obj = self.model_voucher.create(vals)
+ line_vals = {
+ 'name': self.apple_product.name,
+ 'product_id': self.apple_product.id,
+ 'price_unit': 500,
+ 'quantity': 10,
+ 'account_id': self.receivable_account.id,
+ 'voucher_id': voucher_obj.id,
+ }
+ self.model_voucher_line.create(line_vals)
+ return voucher_obj
+
diff --git a/addons/account_voucher/tests/test_voucher_receipt_branch.py b/addons/account_voucher/tests/test_voucher_receipt_branch.py
new file mode 100644
index 00000000..18ed6de0
--- /dev/null
+++ b/addons/account_voucher/tests/test_voucher_receipt_branch.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+
+from . import test_account_voucher_branch as test_branch
+
+
+class TestAccountVoucherReceiptBranch(test_branch.TestAccountVoucherBranch):
+
+ def test_account_receipt_voucher(self):
+
+ self.account_voucher_1.proforma_voucher()
+ self.account_voucher_2.proforma_voucher()
+
+ branch_1_ids = all(line_id_1.branch_id.id == self.account_voucher_1.branch_id.id
+ for line_id_1 in self.account_voucher_1.move_id.line_ids)
+ branch_2_ids = all(line_id_2.branch_id.id == self.account_voucher_2.branch_id.id
+ for line_id_2 in self.account_voucher_2.move_id.line_ids)
+ self.assertNotEqual(branch_1_ids, False, 'Journal Entries of receipt has different branch id.')
+ self.assertNotEqual(branch_2_ids, False, 'Journal Entries of receipt has different branch id.')
+
+ self.account_move = self.env['account.move']
+ account_move_id_1 = self.account_voucher_1.move_id
+ account_move_id_2 = self.account_voucher_2.move_id
+ account_move_ids = self.account_move.sudo(self.branch_user_1).search(
+ [('id', 'in', [account_move_id_1.id, account_move_id_2.id])])
+ self.assertEqual(len(account_move_ids), 2)
+
+ account_move_ids = self.account_move.sudo(self.branch_user_2).search(
+ [('id', 'in', [account_move_id_1.id, account_move_id_2.id]),
+ ('branch_id', '=', self.branch_2.id)])
+ self.assertEqual(len(account_move_ids), 1)
diff --git a/addons/account_voucher/views/account_voucher_views.xml b/addons/account_voucher/views/account_voucher_views.xml
index 4e0170cb..ea2f3371 100644
--- a/addons/account_voucher/views/account_voucher_views.xml
+++ b/addons/account_voucher/views/account_voucher_views.xml
@@ -233,6 +233,7 @@
+
@@ -346,6 +347,7 @@
+
diff --git a/addons/analytic/models/analytic_account.py b/addons/analytic/models/analytic_account.py
index 531d1cb6..d3c65f4e 100644
--- a/addons/analytic/models/analytic_account.py
+++ b/addons/analytic/models/analytic_account.py
@@ -14,7 +14,7 @@ class AccountAnalyticTag(models.Model):
class AccountAnalyticAccount(models.Model):
_name = 'account.analytic.account'
- _inherit = ['mail.thread']
+ _inherit = ['mail.thread', 'ir.branch.company.mixin']
_description = 'Analytic Account'
_order = 'code, name asc'
@@ -105,4 +105,6 @@ class AccountAnalyticLine(models.Model):
tag_ids = fields.Many2many('account.analytic.tag', 'account_analytic_line_tag_rel', 'line_id', 'tag_id', string='Tags', copy=True)
company_id = fields.Many2one(related='account_id.company_id', string='Company', store=True, readonly=True)
+ branch_id = fields.Many2one(related='account_id.branch_id',
+ string='Branch', store=True, readonly=True)
currency_id = fields.Many2one(related="company_id.currency_id", string="Currency", readonly=True)
diff --git a/addons/analytic/security/analytic_security.xml b/addons/analytic/security/analytic_security.xml
index bc6f92f2..95538733 100644
--- a/addons/analytic/security/analytic_security.xml
+++ b/addons/analytic/security/analytic_security.xml
@@ -15,6 +15,14 @@
['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]
+
+
+ Analytic Multi Branch
+
+
+ ['|',('branch_id','=', False),'|',('branch_id','=',user.default_branch_id.id), ('branch_id','in', [b.id for b in user.branch_ids])]
+
+
diff --git a/addons/analytic/views/analytic_account_views.xml b/addons/analytic/views/analytic_account_views.xml
index a930635a..6a2763fa 100644
--- a/addons/analytic/views/analytic_account_views.xml
+++ b/addons/analytic/views/analytic_account_views.xml
@@ -51,6 +51,7 @@
+
@@ -112,6 +113,7 @@
+
@@ -136,6 +138,7 @@
+
@@ -229,6 +232,7 @@
+
diff --git a/addons/base_branch_company/demo/branch_demo.xml b/addons/base_branch_company/demo/branch_demo.xml
index e71453f1..e9d580fc 100644
--- a/addons/base_branch_company/demo/branch_demo.xml
+++ b/addons/base_branch_company/demo/branch_demo.xml
@@ -38,5 +38,10 @@
+
+
+
+
+
diff --git a/addons/sale/data/sale_demo.xml b/addons/sale/data/sale_demo.xml
index 95120bda..e87de53e 100644
--- a/addons/sale/data/sale_demo.xml
+++ b/addons/sale/data/sale_demo.xml
@@ -328,6 +328,7 @@
+
@@ -338,6 +339,7 @@
Laptop E5023
+
3
2450.00
@@ -345,6 +347,7 @@
+
Mouse, Wireless
3
@@ -354,6 +357,7 @@
+
@@ -364,6 +368,7 @@
+
Laptop E5023
1
@@ -373,6 +378,7 @@
+
Mouse, Wireless
2
@@ -381,6 +387,7 @@
+
@@ -392,6 +399,7 @@
+
Laptop E5023
1
@@ -401,6 +409,7 @@
+
Mouse, Wireless
1
@@ -411,6 +420,7 @@
+
@@ -420,6 +430,7 @@
+
Laptop E5023
4
@@ -429,6 +440,7 @@
+
Mouse, Wireless
4
@@ -438,6 +450,7 @@
+
@@ -448,6 +461,7 @@
+
Laptop E5023
4
@@ -457,6 +471,7 @@
+
Mouse, Wireless
3
@@ -466,6 +481,7 @@
+
@@ -476,6 +492,7 @@
+
Laptop E5023
3
@@ -485,6 +502,7 @@
+
Mouse, Wireless
3
@@ -494,6 +512,7 @@
+
@@ -504,6 +523,7 @@
+
Laptop E5023
2
@@ -513,6 +533,7 @@
+
Mouse, Wireless
2
@@ -522,6 +543,7 @@
+
@@ -532,6 +554,7 @@
+
Laptop E5023
2
@@ -541,6 +564,7 @@
+
Mouse, Wireless
2
diff --git a/addons/sale/models/sale.py b/addons/sale/models/sale.py
index abb7a771..49d94d2e 100644
--- a/addons/sale/models/sale.py
+++ b/addons/sale/models/sale.py
@@ -13,13 +13,14 @@ from flectra.osv import expression
from flectra.tools import float_is_zero, float_compare, DEFAULT_SERVER_DATETIME_FORMAT
from flectra.tools.misc import formatLang
-
+from flectra.exceptions import ValidationError
from flectra.addons import decimal_precision as dp
class SaleOrder(models.Model):
_name = "sale.order"
- _inherit = ['mail.thread', 'mail.activity.mixin', 'portal.mixin']
+ _inherit = ['mail.thread', 'mail.activity.mixin', 'portal.mixin',
+ 'ir.branch.company.mixin']
_description = "Quotation"
_order = 'date_order desc, id desc'
@@ -164,6 +165,17 @@ class SaleOrder(models.Model):
product_id = fields.Many2one('product.product', related='order_line.product_id', string='Product')
+ @api.multi
+ @api.constrains('branch_id', 'company_id')
+ def _check_company_branch(self):
+ for order in self:
+ if order.branch_id and order.company_id != order.branch_id.company_id:
+ raise ValidationError(_(
+ 'Configuration Error of Company:\n'
+ 'The Sales Order Company (%s) and the Company (%s) of '
+ 'Branch must be the same!') % (
+ order.company_id.name, order.branch_id.company_id.name))
+
def _compute_portal_url(self):
super(SaleOrder, self)._compute_portal_url()
for order in self:
@@ -354,6 +366,7 @@ class SaleOrder(models.Model):
invoice_vals = {
'name': self.client_order_ref or '',
'origin': self.name,
+ 'branch_id': self.branch_id and self.branch_id.id,
'type': 'out_invoice',
'account_id': self.partner_invoice_id.property_account_receivable_id.id,
'partner_id': self.partner_invoice_id.id,
@@ -548,6 +561,7 @@ class SaleOrder(models.Model):
analytic = self.env['account.analytic.account'].create({
'name': name,
'code': order.client_order_ref,
+ 'branch_id': order.branch_id and order.branch_id.id,
'company_id': order.company_id.id,
'partner_id': order.partner_id.id
})
@@ -907,6 +921,8 @@ class SaleOrderLine(models.Model):
salesman_id = fields.Many2one(related='order_id.user_id', store=True, string='Salesperson', readonly=True)
currency_id = fields.Many2one(related='order_id.currency_id', store=True, string='Currency', readonly=True)
company_id = fields.Many2one(related='order_id.company_id', string='Company', store=True, readonly=True)
+ branch_id = fields.Many2one(related='order_id.branch_id',
+ string='Branch', store=True, readonly=True)
order_partner_id = fields.Many2one(related='order_id.partner_id', store=True, string='Customer')
analytic_tag_ids = fields.Many2many('account.analytic.tag', string='Analytic Tags')
is_downpayment = fields.Boolean(
@@ -953,6 +969,7 @@ class SaleOrderLine(models.Model):
'name': self.name,
'sequence': self.sequence,
'origin': self.order_id.name,
+ 'branch_id': self.branch_id and self.branch_id.id,
'account_id': account.id,
'price_unit': self.price_unit,
'quantity': qty,
diff --git a/addons/sale/report/report_all_channels_sales.py b/addons/sale/report/report_all_channels_sales.py
index 2ae32b0e..aff06e38 100644
--- a/addons/sale/report/report_all_channels_sales.py
+++ b/addons/sale/report/report_all_channels_sales.py
@@ -6,6 +6,7 @@ from flectra import api, fields, models, tools
class PosSaleReport(models.Model):
_name = "report.all.channels.sales"
+ _inherit = ['ir.branch.company.mixin']
_description = "All sales orders grouped by sales channels"
_auto = False
@@ -37,6 +38,7 @@ class PosSaleReport(models.Model):
so.user_id AS user_id,
pt.categ_id AS categ_id,
so.company_id AS company_id,
+ so.branch_id AS branch_id,
sol.price_total / COALESCE(cr.rate, 1.0) AS price_total,
so.pricelist_id AS pricelist_id,
rp.country_id AS country_id,
@@ -75,6 +77,7 @@ class PosSaleReport(models.Model):
user_id,
categ_id,
company_id,
+ branch_id,
price_total,
pricelist_id,
analytic_account_id,
diff --git a/addons/sale/report/sale_report.py b/addons/sale/report/sale_report.py
index 40508686..e45dcfa0 100644
--- a/addons/sale/report/sale_report.py
+++ b/addons/sale/report/sale_report.py
@@ -7,6 +7,7 @@ from flectra import api, fields, models
class SaleReport(models.Model):
_name = "sale.report"
+ _inherit = ['ir.branch.company.mixin']
_description = "Sales Orders Statistics"
_auto = False
_rec_name = 'date'
@@ -68,6 +69,7 @@ class SaleReport(models.Model):
s.partner_id as partner_id,
s.user_id as user_id,
s.company_id as company_id,
+ s.branch_id as branch_id,
extract(epoch from avg(date_trunc('day',s.date_order)-date_trunc('day',s.create_date)))/(24*60*60)::decimal(16,2) as delay,
t.categ_id as categ_id,
s.pricelist_id as pricelist_id,
@@ -111,6 +113,7 @@ class SaleReport(models.Model):
s.user_id,
s.state,
s.company_id,
+ s.branch_id,
s.pricelist_id,
s.analytic_account_id,
s.team_id,
diff --git a/addons/sale/report/sale_report_templates.xml b/addons/sale/report/sale_report_templates.xml
index 7818fef7..566fff4f 100644
--- a/addons/sale/report/sale_report_templates.xml
+++ b/addons/sale/report/sale_report_templates.xml
@@ -60,6 +60,10 @@
Payment Terms:
+
diff --git a/addons/sale/security/sale_security.xml b/addons/sale/security/sale_security.xml
index 69ac5c76..92de2e97 100644
--- a/addons/sale/security/sale_security.xml
+++ b/addons/sale/security/sale_security.xml
@@ -84,6 +84,30 @@
['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]
+
+
+
+
+ Sales Order multi-branch
+
+
+ ['|',('branch_id','=', False),'|',('branch_id','=',user.default_branch_id.id), ('branch_id','in', [b.id for b in user.branch_ids])]
+
+
+
+ Sales Order Line multi-branch
+
+
+ ['|',('branch_id','=', False),'|',('branch_id','=',user.default_branch_id.id), ('branch_id','in', [b.id for b in user.branch_ids])]
+
+
+
+ Sales Order Analysis multi-branch
+
+
+ ['|',('branch_id','=', False),'|',('branch_id','=',user.default_branch_id.id), ('branch_id','in', [b.id for b in user.branch_ids])]
+
+
Portal Personal Quotations/Sales Orders
diff --git a/addons/sale/tests/__init__.py b/addons/sale/tests/__init__.py
index c60d2736..81513778 100644
--- a/addons/sale/tests/__init__.py
+++ b/addons/sale/tests/__init__.py
@@ -4,3 +4,4 @@ from . import test_sale_to_invoice
from . import test_sale_order
from . import test_product_id_change
from . import test_sale_to_invoice_and_to_be_invoiced
+from . import test_sale_branch
diff --git a/addons/sale/tests/test_sale_branch.py b/addons/sale/tests/test_sale_branch.py
new file mode 100644
index 00000000..1e9dba62
--- /dev/null
+++ b/addons/sale/tests/test_sale_branch.py
@@ -0,0 +1,121 @@
+# -*- coding: utf-8 -*-
+from flectra.tests import common
+
+
+class TestSaleBranch(common.TransactionCase):
+ def setUp(self):
+ super(TestSaleBranch, self).setUp()
+
+ self.sale_obj = self.env['sale.order']
+
+ self.main_company = self.env.ref('base.main_company')
+
+ self.payment_model_obj = self.env['sale.advance.payment.inv']
+
+ self.sale_user_group = self.env.ref('sales_team.group_sale_manager')
+ self.account_user_group = self.env.ref('account.group_account_invoice')
+ self.branch_1 = self.env.ref('base_branch_company.data_branch_1')
+ self.branch_2 = self.env.ref('base_branch_company.data_branch_2')
+ self.branch_3 = self.env.ref('base_branch_company.data_branch_3')
+
+ self.sale_customer = self.env.ref('base.res_partner_2')
+ self.sale_pricelist = self.env.ref('product.list0')
+
+ self.apple_product = self.env.ref('product.product_product_7')
+ self.apple_product.write({'invoice_policy': 'order'})
+
+ self.user_1 = self.create_sale_user(
+ self.main_company, 'user_1', self.branch_1,
+ [self.branch_1, self.branch_3],
+ [self.sale_user_group, self.account_user_group])
+ self.user_2 = self.create_sale_user(
+ self.main_company, 'user_2', self.branch_3,
+ [self.branch_3], [self.sale_user_group, self.account_user_group])
+
+ self.so_1 = self.create_so(
+ self.sale_customer, self.apple_product, self.user_1.id,
+ self.branch_1, self.sale_pricelist)
+ self.so_2 = self.create_so(
+ self.sale_customer, self.apple_product, self.user_2.id,
+ self.branch_3, self.sale_pricelist)
+
+ def create_sale_user(self, main_company, user_name,
+ branch_id, branch_ids, groups):
+ group_ids = [grp.id for grp in groups]
+ data = {
+ 'company_ids': [(4, main_company.id)],
+ 'branch_ids': [(4, ou.id) for ou in branch_ids],
+ 'company_id': main_company.id,
+ 'groups_id': [(6, 0, group_ids)],
+ 'default_branch_id': branch_id.id,
+ 'login': user_name,
+ 'name': 'Ron Sales User',
+ 'password': '123',
+ 'email': 'ron@yourcompany.com',
+
+ }
+ user_obj = self.env['res.users'].create(data)
+ return user_obj
+
+ def create_so(self, customer_id, product_id,
+ user_id, branch_id, pricelist_id):
+ data = {
+ 'partner_id': customer_id.id,
+ 'branch_id': branch_id.id,
+ 'pricelist_id': pricelist_id.id,
+ 'partner_shipping_id': customer_id.id,
+ 'partner_invoice_id': customer_id.id,
+ }
+ sale_id = self.sale_obj.sudo(user_id).create(data)
+ self.env['sale.order.line'].sudo(user_id).create({
+ 'order_id': sale_id.id,
+ 'product_id': product_id.id,
+ 'name': 'Order Line'
+ })
+ return sale_id
+
+ def sale_order_confirm(self, sale_obj):
+ context = {
+ 'open_invoices': True,
+ 'active_id': sale_obj.id,
+ 'active_model': 'sale.order',
+ 'active_ids': sale_obj.ids,
+
+ }
+ sale_obj.action_confirm()
+
+ invoice = self.payment_model_obj.create({
+ 'advance_payment_method': 'all',
+ })
+
+ result = invoice.with_context(context).create_invoices()
+ invoice = result['res_id']
+ return invoice
+
+ def get_sale_order(self, sale_order_id, branch_id):
+ sale = self.sale_obj.sudo(self.user_2.id).search(
+ [('id', '=', sale_order_id),
+ ('branch_id', '=', branch_id)])
+ return sale
+
+ def test_user_authentication(self):
+ sale = self.get_sale_order(self.so_1.id, self.branch_1.id)
+ self.assertEqual(sale.ids, [], 'Test User 2 should not have access to '
+ 'Branch %s' % self.branch_1.name)
+ self.sale_order_confirm(self.so_1)
+ branch_3_invoice_id = self.sale_order_confirm(self.so_2)
+ branch_3 = self.env['account.invoice'].sudo(self.user_2.id).search(
+ [('id', '=', branch_3_invoice_id),
+ ('branch_id', '=', self.branch_3.id)])
+ self.assertNotEqual(branch_3.ids, [],
+ 'Invoice should have branch_3 Branch')
+
+ def test_user_authentication_2(self):
+ sale = self.get_sale_order(self.so_1.id, self.branch_1.id)
+ self.assertEqual(sale.ids, [], 'Test User 2 should '
+ 'not have access to Branch %s'
+ % self.branch_1.name)
+ sale = self.get_sale_order(self.so_2.id, self.branch_3.id)
+ self.assertEqual(len(sale.ids), 1, 'Test User 1 should'
+ ' have access to Branch : %s'
+ % self.branch_3.name)
diff --git a/addons/sale/views/sale_views.xml b/addons/sale/views/sale_views.xml
index 3ab72bf3..620e5aca 100644
--- a/addons/sale/views/sale_views.xml
+++ b/addons/sale/views/sale_views.xml
@@ -381,6 +381,7 @@
+
diff --git a/addons/sales_team/models/crm_team.py b/addons/sales_team/models/crm_team.py
index c44ee16a..0db4c6c9 100644
--- a/addons/sales_team/models/crm_team.py
+++ b/addons/sales_team/models/crm_team.py
@@ -84,7 +84,7 @@ class CrmTeam(models.Model):
@api.constrains('company_id', 'branch_id')
def _check_company_branch(self):
for record in self:
- if record.company_id and record.company_id != record.branch_id.company_id:
+ if record.branch_id and record.company_id and record.company_id != record.branch_id.company_id:
raise ValidationError(
_('Configuration Error of Company:\n'
'The Company (%s) in the Team and '