[ADD] inter_company_rules : feature to create inter company sale, purchase and invoice (Example : automatically create PO or SO for supplier company of the same database).

This commit is contained in:
tpa-odoo
2015-02-05 13:25:44 +01:00
committed by Jérome Maes
parent 275234c067
commit 88200e42e9
16 changed files with 912 additions and 0 deletions
+2
View File
@@ -43,6 +43,8 @@ class base_config_settings(osv.osv_memory):
help="""This installs the module google_calendar."""),
'font': fields.many2one('res.font', string="Report Font", domain=[('mode', 'in', ('Normal', 'Regular', 'all', 'Book'))],
help="Set the font into the report header, it will be used as default font in the RML reports of the user company"),
'module_inter_company_rules': fields.boolean('Manage Inter Company',
help="""This installs the module inter_company_rules.\n Configure company rules to automatically create SO/PO when one of your company sells/buys to another of your company."""),
'company_share_partner': fields.boolean('Share partners to all companies',
help="Share your partners to all companies defined in your instance.\n"
" * Checked : Partners are visible for every companies, even if a company is defined on the partner.\n"
+9
View File
@@ -104,6 +104,15 @@
</div>
</div>
</group>
<group>
<label for="module_inter_company_rules" string="Inter company"/>
<div name="inter_company">
<div name="module_inter_company_rules">
<field name="module_inter_company_rules" class="oe_inline"/>
<label for="module_inter_company_rules"/>
</div>
</div>
</group>
<group string="Shared ressources" name="company_share_ressources">
<label for="company_share_partner" />
<div>
+2
View File
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
import models
+50
View File
@@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2013-Today OpenERP SA (<http://www.openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
#
##############################################################################
{
'name': 'Inter Company Module for Sale/Purchase Orders and Invoices',
'version': '1.1',
'summary': 'Intercompany SO/PO/INV rules',
'description': ''' Module for synchronization of Documents between several companies. For example, this allow you to have a Sale Order created automatically when a Purchase Order is validated with another company of the system as supplier, and inversely.
Supported documents are SO, PO and invoices/refunds.
''',
'author': 'Odoo SA',
'website': 'http://www.odoo.com',
'depends': [
'sale',
'purchase',
'sale_stock',
'sale_order_dates'
],
'data': [
'views/inter_company_so_po_view.xml',
'views/res_config_view.xml',
],
'test': [
'test/test_intercompany_data.yml',
'test/inter_company_so_to_po.yml',
'test/inter_company_po_to_so.yml',
'test/inter_company_invoice.yml'
],
'demo': [],
'installable': True,
'auto_install': False,
}
@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
import res_config
import res_company
import purchase_order
import sale_order
import account_invoice
@@ -0,0 +1,133 @@
# -*- coding: utf-8 -*-
from openerp import api, fields, models, _
from openerp.exceptions import Warning
class account_invoice(models.Model):
_inherit = 'account.invoice'
auto_generated = fields.Boolean(string='Auto Generated Document', copy=False, default=False)
auto_invoice_id = fields.Many2one('account.invoice', string='Source Invoice', readonly=True, copy=False)
@api.multi
def invoice_validate(self):
""" Validated invoice generate cross invoice base on company rules """
for invoice in self:
# do not consider invoices that have already been auto-generated, nor the invoices that were already validated in the past
company = self.env['res.company']._find_company_from_partner(invoice.partner_id.id)
if company and company.auto_generate_invoices and not invoice.auto_generated:
if invoice.type == 'out_invoice':
invoice.inter_company_create_invoice(company, 'in_invoice', 'purchase')
elif invoice.type == 'in_invoice':
invoice.inter_company_create_invoice(company, 'out_invoice', 'sale')
elif invoice.type == 'out_refund':
invoice.inter_company_create_invoice(company, 'in_refund', 'purchase_refund')
elif invoice.type == 'in_refund':
invoice.inter_company_create_invoice(company, 'out_refund', 'sale_refund')
return super(account_invoice, self).invoice_validate()
@api.one
def inter_company_create_invoice(self, company, inv_type, journal_type):
""" create an invoice for the given company : it wil copy the invoice lines in the new
invoice. The intercompany user is the author of the new invoice.
:param company : the company of the created invoice
:rtype company : res.company record
:param inv_type : the type of the invoice ('in_refund', 'out_refund', 'in_invoice', ...)
:rtype inv_type : string
:param journal_type : the type of the journal to register the invoice
:rtype journal_type : string
"""
# find user for creating the invoice from company
intercompany_uid = company.intercompany_user_id and company.intercompany_user_id.id or False
if not intercompany_uid:
raise Warning(_('Provide one user for intercompany relation for % ') % company.name)
context = self._context.copy()
context['force_company'] = company.id
origin_partner_id = self.company_id.partner_id
invoice_line_ids = []
for line in self.invoice_line:
# get invoice line data from product onchange
product_uom_id = line.product_id.uom_id and line.product_id.uom_id.id or False
line_data = line.with_context(context).sudo().product_id_change(line.product_id.id,
product_uom_id,
qty=line.quantity,
name='',
type=inv_type,
partner_id=origin_partner_id.id,
fposition_id=origin_partner_id.property_account_position.id,
company_id=company.id)
# create invoice line, as the intercompany user
inv_line_data = self.sudo()._prepare_invoice_line_data(line_data, line)
inv_line_id = line.with_context(context).sudo(intercompany_uid).create(inv_line_data)
invoice_line_ids.append(inv_line_id.id)
# create invoice, as the intercompany user
invoice_vals = self.with_context(context).sudo()._prepare_invoice_data(invoice_line_ids, inv_type, journal_type, company)[0]
return self.with_context(context).sudo(intercompany_uid).create(invoice_vals)
@api.one
def _prepare_invoice_data(self, invoice_line_ids, inv_type, journal_type, company):
""" Generate invoice values
:param invoice_line_ids : the ids of the invoice lines
:rtype invoice_line_ids : array of integer
:param inv_type : the type of the invoice to prepare the values
:param journal_type : type of the journal to register the invoice_line_ids
:rtype journal_type : string
:rtype company : res.company record
"""
# find the correct journal
journal = self.env['account.journal'].search([('type', '=', journal_type), ('company_id', '=', company.id)], limit=1)
if not journal:
raise Warning(_('Please define %s journal for this company: "%s" (id:%d).') % (journal_type, company.name, company.id))
# find periods of supplier company
context = self._context.copy()
context['company_id'] = company.id
period_ids = self.env['account.period'].with_context(context).find(self.date_invoice)
# find account, payment term, fiscal position, bank.
partner_data = self.onchange_partner_id(inv_type, self.company_id.partner_id.id, company_id=company.id)
return {
'name': self.name,
#TODO : not sure !!
'origin': self.company_id.name + _(' Invoice: ') + str(self.number),
'type': inv_type,
'date_invoice': self.date_invoice,
'reference': self.reference,
'account_id': partner_data['value'].get('account_id', False),
'partner_id': self.company_id.partner_id.id,
'journal_id': journal.id,
'invoice_line': [(6, 0, invoice_line_ids)],
'currency_id': self.currency_id and self.currency_id.id,
'fiscal_position': partner_data['value'].get('fiscal_position', False),
'payment_term': partner_data['value'].get('payment_term', False),
'company_id': company.id,
'period_id': period_ids and period_ids[0].id or False,
'partner_bank_id': partner_data['value'].get('partner_bank_id', False),
'auto_generated': True,
'auto_invoice_id': self.id,
}
@api.model
def _prepare_invoice_line_data(self, line_data, line):
""" Generate invoice line values
:param line_data : dict of invoice line data
:rtype line_data : dict
:param line : the invoice line object
:rtype line : account.invoice.line record
"""
vals = {
'name': line.name,
'price_unit': line.price_unit,
'quantity': line.quantity,
'discount': line.discount,
'product_id': line.product_id.id or False,
'uos_id': line.uos_id.id or False,
'sequence': line.sequence,
'invoice_line_tax_id': [(6, 0, line_data['value'].get('invoice_line_tax_id', []))],
'account_analytic_id': line.account_analytic_id.id or False,
}
if line_data['value'].get('account_id'):
vals['account_id'] = line_data['value']['account_id']
return vals
@@ -0,0 +1,118 @@
# -*- coding: utf-8 -*-
from openerp import api, fields, models, _
from openerp.exceptions import Warning
class purchase_order(models.Model):
_inherit = "purchase.order"
auto_generated = fields.Boolean(string='Auto Generated Purchase Order', copy=False)
auto_sale_order_id = fields.Many2one('sale.order', string='Source Sale Order', readonly=True, copy=False)
@api.multi
def wkf_confirm_order(self):
""" Generate inter company sale order base on conditions."""
res = super(purchase_order, self).wkf_confirm_order()
for order in self:
# get the company from partner then trigger action of intercompany relation
company_rec = self.env['res.company']._find_company_from_partner(order.partner_id.id)
if company_rec and company_rec.so_from_po and (not order.auto_generated):
order.inter_company_create_sale_order(company_rec)
return res
@api.one
def inter_company_create_sale_order(self, company):
""" Create a Sale Order from the current PO (self)
Note : In this method, reading the current PO is done as sudo, and the creation of the derived
SO as intercompany_user, minimizing the access right required for the trigger user.
:param company : the company of the created PO
:rtype company : res.company record
"""
SaleOrder = self.env['sale.order']
company_partner = self.company_id.partner_id
# find user for creating and validation SO/PO from partner company
intercompany_uid = company.intercompany_user_id and company.intercompany_user_id.id or False
if not intercompany_uid:
raise Warning(_('Provide at least one user for inter company relation for % ') % company.name)
# check intercompany user access rights
if not SaleOrder.sudo(intercompany_uid).check_access_rights('create', raise_exception=False):
raise Warning(_("Inter company user of company %s doesn't have enough access rights") % company.name)
# check pricelist currency should be same with SO/PO document
if self.pricelist_id.currency_id.id != company_partner.property_product_pricelist.currency_id.id:
raise Warning(_('You cannot create SO from PO because sale price list currency is different than purchase price list currency.'))
# create the SO and generate its lines from the PO lines
SaleOrderLine = self.env['sale.order.line']
# read it as sudo, because inter-compagny user can not have the access right on PO
sale_order_data = self.sudo()._prepare_sale_order_data(self.name, company_partner, company, self.dest_address_id and self.dest_address_id.id or False)
sale_order = SaleOrder.sudo(intercompany_uid).create(sale_order_data[0])
for line in self.order_line:
so_line_vals = self.sudo()._prepare_sale_order_line_data(line, company, sale_order.id)
SaleOrderLine.sudo(intercompany_uid).create(so_line_vals)
# write supplier reference field on PO
if not self.partner_ref:
self.partner_ref = sale_order.name
#Validation of sale order
if company.auto_validation:
sale_order.sudo(intercompany_uid).signal_workflow('order_confirm')
@api.one
def _prepare_sale_order_data(self, name, partner, company, direct_delivery_address):
""" Generate the Sale Order values from the PO
:param name : the origin client reference
:rtype name : string
:param partner : the partner reprenseting the company
:rtype partner : res.partner record
:param company : the company of the created SO
:rtype company : res.company record
:param direct_delivery_address : the address of the SO
:rtype direct_delivery_address : res.partner record
"""
partner_addr = partner.sudo().address_get(['default', 'invoice', 'delivery', 'contact'])
return {
'name': self.env['ir.sequence'].sudo().next_by_code('sale.order') or '/',
'company_id': company.id,
'client_order_ref': name,
'partner_id': partner.id,
'pricelist_id': partner.property_product_pricelist.id,
'partner_invoice_id': partner_addr['invoice'],
'date_order': self.date_order,
'fiscal_position': partner.property_account_position and partner.property_account_position.id or False,
'user_id': False,
'auto_generated': True,
'auto_purchase_order_id': self.id,
'partner_shipping_id': direct_delivery_address or partner_addr['delivery']
}
@api.model
def _prepare_sale_order_line_data(self, line, company, sale_id):
""" Generate the Sale Order Line values from the PO line
:param line : the origin Purchase Order Line
:rtype line : purchase.order.line record
:param company : the company of the created SO
:rtype company : res.company record
:param sale_id : the id of the SO
"""
# it may not affected because of parallel company relation
price = line.price_unit or 0.0
taxes = line.taxes_id
if line.product_id:
taxes = line.product_id.taxes_id
company_taxes = [tax_rec.id for tax_rec in taxes if tax_rec.company_id.id == company.id]
return {
'name': line.product_id and line.product_id.name or line.name,
'order_id': sale_id,
'product_uom_qty': line.product_qty,
'product_id': line.product_id and line.product_id.id or False,
'product_uom': line.product_id and line.product_id.uom_id.id or line.product_uom.id,
'price_unit': price,
'delay': line.product_id and line.product_id.sale_delay or 0.0,
'company_id': company.id,
'tax_id': [(6, 0, company_taxes)],
}
@@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
from openerp import api, fields, models, _, SUPERUSER_ID
from openerp.exceptions import Warning
class res_company(models.Model):
_inherit = 'res.company'
so_from_po = fields.Boolean(string="Create Sale Orders when buying to this company",
help="Generate a Sale Order when a Purchase Order with this company as supplier is created.\n The intercompany user must at least be Sale User.")
po_from_so = fields.Boolean(string="Create Purchase Orders when selling to this company",
help="Generate a Purchase Order when a Sale Order with this company as customer is created.\n The intercompany user must at least be Purchase User.")
auto_generate_invoices = fields.Boolean(string="Create Invoices/Refunds when encoding invoices/refunds made to this company",
help="Generate Customer/Supplier Invoices (and refunds) when encoding invoices (or refunds) made to this company.\n e.g: Generate a Customer Invoice when a Supplier Invoice with this company as supplier is created.")
auto_validation = fields.Boolean(string="Sale/Purchase Orders Auto Validation",
help="When a Sale Order or a Purchase Order is created by a multi company rule for this company, it will automatically validate it")
intercompany_user_id = fields.Many2one("res.users", string="Inter Company User", default=SUPERUSER_ID,
help="Responsible user for creation of documents triggered by intercompany rules.")
warehouse_id = fields.Many2one("stock.warehouse", string="Warehouse For Purchase Orders",
help="Default value to set on Purchase Orders that will be created based on Sale Orders made to this company")
@api.model
def _find_company_from_partner(self, partner_id):
company = self.sudo().search([('partner_id', '=', partner_id)], limit=1)
return company or False
@api.one
@api.constrains('po_from_so', 'so_from_po', 'auto_generate_invoices')
def _check_intercompany_missmatch_selection(self):
if (self.po_from_so or self.so_from_po) and self.auto_generate_invoices:
raise Warning(_('''You cannot select to create invoices based on other invoices
simultaneously with another option ('Create Sale Orders when buying to this
company' or 'Create Purchase Orders when selling to this company')!'''))
@@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
from openerp import models, fields, api
class inter_company_rules_configuration(models.TransientModel):
_inherit = 'base.config.settings'
company_id = fields.Many2one('res.company', string='Select Company',
help='Select company to setup Inter company rules.')
rule_type = fields.Selection([('so_and_po', 'SO and PO setting for inter company'),
('invoice_and_refunds', 'Create Invoice/Refunds when encoding invoice/refunds')],
help='Select the type to setup inter company rules in selected company.')
so_from_po = fields.Boolean(string='Create Sale Orders when buying to this company',
help='Generate a Sale Order when a Purchase Order with this company as supplier is created.')
po_from_so = fields.Boolean(string='Create Purchase Orders when selling to this company',
help='Generate a Purchase Order when a Sale Order with this company as customer is created.')
auto_validation = fields.Boolean(string='Sale/Purchase Orders Auto Validation',
help='''When a Sale Order or a Purchase Order is created by a multi
company rule for this company, it will automatically validate it.''')
warehouse_id = fields.Many2one('stock.warehouse', string='Warehouse For Purchase Orders',
help='Default value to set on Purchase Orders that will be created based on Sale Orders made to this company.')
@api.onchange('rule_type')
def onchange_rule_type(self):
if self.rule_type == 'invoice_and_refunds':
self.so_from_po = False
self.po_from_so = False
self.auto_validation = False
elif self.rule_type == 'so_and_po':
self.invoice_and_refunds = False
@api.onchange('company_id')
def onchange_company_id(self):
if self.company_id:
rule_type = False
if self.company_id.so_from_po or self.company_id.po_from_so or self.company_id.auto_validation:
rule_type = 'so_and_po'
elif self.company_id.auto_generate_invoices:
rule_type = 'invoice_and_refunds'
self.rule_type = rule_type
self.so_from_po = self.company_id.so_from_po
self.po_from_so = self.company_id.po_from_so
self.auto_validation = self.company_id.auto_validation
self.warehouse_id = self.company_id.warehouse_id.id
@api.multi
def set_inter_company_configuration(self):
if self.company_id:
vals = {
'so_from_po': self.so_from_po,
'po_from_so': self.po_from_so,
'auto_validation': self.auto_validation,
'auto_generate_invoices': True if self.rule_type == 'invoice_and_refunds' else False,
'warehouse_id': self.warehouse_id.id
}
self.company_id.write(vals)
@@ -0,0 +1,127 @@
# -*- coding: utf-8 -*-
from openerp import api, fields, models, _
from openerp.exceptions import Warning
class sale_order(models.Model):
_inherit = "sale.order"
auto_generated = fields.Boolean(string='Auto Generated Sale Order', copy=False)
auto_purchase_order_id = fields.Many2one('purchase.order', string='Source Purchase Order', readonly=True, copy=False)
@api.multi
def action_button_confirm(self):
""" Generate inter company purchase order based on conditions """
res = super(sale_order, self).action_button_confirm()
for order in self:
if not order.company_id: # if company_id not found, return to normal behavior
continue
# if company allow to create a Purchase Order from Sale Order, then do it !
company = self.env['res.company']._find_company_from_partner(order.partner_id.id)
if company and company.po_from_so and (not order.auto_generated):
order.inter_company_create_purchase_order(company)
return res
@api.one
def inter_company_create_purchase_order(self, company):
""" Create a Purchase Order from the current SO (self)
Note : In this method, reading the current SO is done as sudo, and the creation of the derived
PO as intercompany_user, minimizing the access right required for the trigger user
:param company : the company of the created PO
:rtype company : res.company record
"""
PurchaseOrder = self.env['purchase.order']
company_partner = self.company_id and self.company_id.partner_id or False
if not company or not company_partner.id:
return
# find user for creating and validating SO/PO from company
intercompany_uid = company.intercompany_user_id and company.intercompany_user_id.id or False
if not intercompany_uid:
raise Warning(_('Provide one user for intercompany relation for % ') % company.name)
# check intercompany user access rights
if not PurchaseOrder.sudo(intercompany_uid).check_access_rights('create', raise_exception=False):
raise Warning(_("Inter company user of company %s doesn't have enough access rights") % company.name)
# check pricelist currency should be same with SO/PO document
if self.pricelist_id.currency_id.id != company_partner.property_product_pricelist_purchase.currency_id.id:
raise Warning(_('You cannot create PO from SO because purchase pricelist currency is different than sale pricelist currency.'))
# create the PO and generate its lines from the SO
PurchaseOrderLine = self.env['purchase.order.line']
# read it as sudo, because inter-compagny user can not have the access right on PO
po_vals = self.sudo()._prepare_purchase_order_data(company, company_partner)
purchase_order = PurchaseOrder.sudo(intercompany_uid).create(po_vals[0])
for line in self.order_line:
po_line_vals = self.sudo()._prepare_purchase_order_line_data(line, self.date_order, purchase_order.id, company)
PurchaseOrderLine.sudo(intercompany_uid).create(po_line_vals)
# write customer reference field on SO
if not self.client_order_ref:
self.client_order_ref = purchase_order.name
# auto-validate the purchase order if needed
if company.auto_validation:
purchase_order.sudo(intercompany_uid).signal_workflow('purchase_confirm')
@api.one
def _prepare_purchase_order_data(self, company, company_partner):
""" Generate purchase order values, from the SO (self)
:param company_partner : the partner representing the company of the SO
:rtype company_partner : res.partner record
:param company : the company in which the PO line will be created
:rtype company : res.company record
"""
# find location and warehouse, pick warehouse from company object
warehouse = company.warehouse_id and company.warehouse_id.company_id.id == company.id and company.warehouse_id or False
if not warehouse:
raise Warning(_('Configure correct warehouse for company(%s) from Menu: Settings/companies/companies' % (company.name)))
return {
'name': self.env['ir.sequence'].sudo().next_by_code('purchase.order'),
'origin': self.name,
'partner_id': company_partner.id,
'location_id': warehouse.lot_stock_id.id,
'pricelist_id': company_partner.property_product_pricelist_purchase.id,
'date_order': self.date_order,
'company_id': company.id,
'fiscal_position': company_partner.property_account_position or False,
'payment_term_id': company_partner.property_supplier_payment_term.id or False,
'auto_generated': True,
'auto_sale_order_id': self.id,
'partner_ref': self.name,
'dest_address_id': self.partner_shipping_id and self.partner_shipping_id.id or False,
}
@api.model
def _prepare_purchase_order_line_data(self, so_line, date_order, purchase_id, company):
""" Generate purchase order line values, from the SO line
:param so_line : origin SO line
:rtype so_line : sale.order.line record
:param date_order : the date of the orgin SO
:param purchase_id : the id of the purchase order
:param company : the company in which the PO line will be created
:rtype company : res.company record
"""
# price on PO so_line should be so_line - discount
price = so_line.price_unit - (so_line.price_unit * (so_line.discount / 100))
# computing Default taxes of so_line. It may not affect because of parallel company relation
taxes = so_line.tax_id
if so_line.product_id:
taxes = so_line.product_id.supplier_taxes_id
# fetch taxes by company not by inter-company user
company_taxes = [tax_rec.id for tax_rec in taxes if tax_rec.company_id.id == company.id]
return {
'name': so_line.name,
'order_id': purchase_id,
'product_qty': so_line.product_uom_qty,
'product_id': so_line.product_id and so_line.product_id.id or False,
'product_uom': so_line.product_id and so_line.product_id.uom_po_id.id or so_line.product_uom.id,
'price_unit': price or 0.0,
'company_id': so_line.order_id.company_id.id,
'date_planned': so_line.order_id.commitment_date or date_order,
'taxes_id': [(6, 0, company_taxes)],
}
@@ -0,0 +1,79 @@
-
In order to check Inter-Company Rule of creating Supplier invoice from Customer invoice
make 'Create Invoices/Refunds when encoding invoices/refunds made' true.
-
!record {model: res.company, id: company_a}:
so_from_po: 0
po_from_so: 0
auto_generate_invoices: 1
-
!record {model: res.company, id: company_b}:
so_from_po: 0
po_from_so: 0
auto_generate_invoices: 1
-
I apply account user group to Company A user.
-
!record {model: res.users, id: res_users_company_a}:
groups_id:
- account.group_account_user
-
I will create customer invoice with account user.
-
!context
uid: 'res_users_company_a'
-
!record {model: account.account, id: account_expense_company_a}:
name: Expenses
code: X1000
type: other
user_type: account.data_account_type_expense
company_id: company_a
-
Create an invoice for the partner of company B with amount 450.0
-
!record {model: account.invoice, id: account_invoice_0}:
company_id: company_a
partner_id: partner_company_b
currency_id: base.EUR
invoice_line:
- product_id: product_consultant
price_unit: 450.0
account_id: account_expense_company_a
quantity: 1.0
-
I check that the customer invoice is in draft state.
-
!assert {model: account.invoice, id: account_invoice_0}:
- state == 'draft'
-
I validate the invoice.
-
!workflow {model: account.invoice, action: invoice_open, ref: account_invoice_0}
-
Now apply account user group to User B and test with that user.
-
!record {model: res.users, id: res_users_company_b}:
groups_id:
- account.group_account_user
-
!context
uid: 'res_users_company_b'
-
I check that the supplier invoice is created with proper data.
-
!python {model: account.invoice}: |
supplier_invoices = self.search(cr, uid, [('type','=','in_invoice')], context=context)
supplier_invoice = self.browse(cr, uid, supplier_invoices[0], context=context)
#check invoice line is created with proper data
assert supplier_invoice.invoice_line[0].quantity == 1, "Quantity in invoice line is incorrect."
assert supplier_invoice.invoice_line[0].product_id.id == ref("product_consultant"), "Product in line is incorrect."
assert supplier_invoice.invoice_line[0].price_unit == 450, "Unit Price in invoice line is incorrect."
assert supplier_invoice.invoice_line[0].account_id.company_id.id == ref("company_b"), "Applied account in created invoice line is not relevant to company."
assert supplier_invoice.state == "draft", "invoice should be in draft state."
assert supplier_invoice.amount_total == 450.0, "Total amount is incorrect."
assert supplier_invoice.company_id.id == ref("company_b"), "Applied company in created invoice is incorrect."
assert supplier_invoice.account_id.company_id.id == ref("company_b"), "Applied account in created invoice is not relevant to company."
@@ -0,0 +1,42 @@
-
In order to check Inter-Company Rule of creating SO from PO, I login with user company A
-
!context
uid: 'res_users_company_a'
-
Quotation of purchase order.
-
!record {model: purchase.order, id: purchase_order_cmpa}:
partner_id: partner_company_b
company_id: company_a
order_line:
- product_id: product_consultant
name: 'Service'
price_unit: 450.0
-
I confirm the order.
-
!workflow {model: purchase.order, action: purchase_confirm, ref: purchase_order_cmpa}
-
Login with user of company B and check sale order.
-
!context
uid: 'res_users_company_b'
-
I check that the Quotation of sale order is created with proper data.
-
!python {model: sale.order}: |
#NOTE: Browse PO with superuser id because of different company of user.
po_name = self.pool.get('purchase.order').browse(cr, 1, ref("purchase_order_cmpa"), context=context).name
sale_order_ids = self.search(cr, uid, [('client_order_ref', '=', po_name)], context=context)
sale_order= self.browse(cr, uid, sale_order_ids[0], context=context)
assert sale_order.state == "draft", "sale order should be in draft state."
assert sale_order.partner_id.id == ref("partner_company_a"), "Supplier is not correspond to Company A."
assert sale_order.company_id.id == ref("company_b"), "Applied company in created sale order is incorrect."
assert sale_order.amount_total == 450.0, "Total amount is incorrect."
#check for order line
assert sale_order.order_line[0].product_id.id == ref("product_consultant"), "Product in line is incorrect."
assert sale_order.order_line[0].name == 'Service', "Product name is incorrect."
assert sale_order.order_line[0].product_uom_qty == 1 , "Product qty is incorrect."
assert sale_order.order_line[0].price_unit == 450, "Unit Price in line is incorrect."
@@ -0,0 +1,46 @@
-
In order to check Inter-Company Rule of creating PO from SO, I login with user company A
-
!context
uid: 'res_users_company_a'
-
I create Quotation of sale order and confirm it.
-
!record {model: sale.order, id: sale_order_cmpa, view: False}:
company_id: company_a
warehouse_id: warehouse_company_a
user_id: res_users_company_a
pricelist_id: 1
partner_id: partner_company_b
partner_invoice_id: partner_company_b
partner_shipping_id: partner_company_b
order_line:
- product_id: product_consultant
name: 'Service'
price_unit: 450.0
-
I confirm the order.
-
!python {model: sale.order}: |
self.action_button_confirm(cr, uid, [ref('sale_order_cmpa')], context=context)
-
I login with user company B.
-
!context
uid: 'res_users_company_b'
-
I check that Quotation of purchase order and order line is same as sale order
-
!python {model: purchase.order}: |
order_ids = self.search(cr, uid, [('company_id','=',ref("company_b"))], context=context)
purchase_order = self.browse(cr, uid, order_ids[0], context=context)
assert purchase_order.state == "draft", "Invoice should be in draft state."
assert purchase_order.partner_id.id == ref("partner_company_a"),"Supplier is not correspond to Company A."
assert purchase_order.company_id.id == ref("company_b"),"Company is not correspond to purchase order."
assert purchase_order.amount_total == 450.0, "Total amount is incorrect."
assert purchase_order.order_line[0].product_id.id == ref("product_consultant"), "Product in line is incorrect."
assert purchase_order.order_line[0].name == 'Service',"Product name is incorrect."
assert purchase_order.order_line[0].price_unit == 450 , "Price unit is incorrect."
assert purchase_order.order_line[0].product_qty == 1 , "Product qty is incorrect."
assert purchase_order.order_line[0].price_subtotal == 450, "line total is incorrect."
@@ -0,0 +1,147 @@
-
!record {model: res.partner, id: partner_company_a, view: False}:
name: Company A
supplier: 1
-
!record {model: res.company, id: company_a, view: False}:
name: Company A
parent_id: base.main_company
currency_id: base.EUR
partner_id: partner_company_a
so_from_po: 1
po_from_so: 1
-
!record {model: stock.location, id: location_stock_company_a, view: False}:
name: Stock - A
usage: internal
company_id: company_a
-
!record {model: stock.location, id: location_output_company_a, view: False}:
name: Output - A
usage: internal
company_id: company_a
-
!record {model: stock.warehouse, id: warehouse_company_a, view: False}:
name: purchase warehouse - A
code: CMPA
wh_input_stock_loc_id: location_stock_company_a
lot_stock_id: location_stock_company_a
wh_output_stock_loc_id: location_output_company_a
company_id: company_a
-
!record {model: res.partner, id: partner_company_b, view: False}:
name: Company B
supplier: 1
-
!record {model: res.company, id: company_b, view: False}:
name: Company B
parent_id: base.main_company
currency_id: base.EUR
partner_id: partner_company_b
so_from_po: 1
po_from_so: 1
-
!record {model: stock.location, id: location_stock_company_b, view: False}:
name: Stock - B
usage: internal
company_id: company_b
-
!record {model: stock.location, id: location_output_company_b, view: False}:
name: Output - B
usage: internal
company_id: company_b
-
!record {model: stock.warehouse, id: warehouse_company_b, view: False}:
name: purchase warehouse - B
code: CMPB
wh_input_stock_loc_id: location_stock_company_b
lot_stock_id: location_stock_company_b
wh_output_stock_loc_id: location_output_company_b
company_id: company_b
-
Apply warehouse in company.
-
!record {model: res.company, id: company_a}:
warehouse_id: warehouse_company_a
-
!record {model: res.company, id: company_b}:
warehouse_id: warehouse_company_b
-
!record {model: product.product, id: product_consultant, view: False}:
name: Service
uom_id: product.product_uom_hour
uom_po_id: product.product_uom_hour
categ_id: product.product_category_all
type: service
company_id: False
-
Create a user for each company
-
!record {model: res.users, id: res_users_company_a, view: False}:
name: User A
login: usera
password: usera
email: usera@yourcompany.com
company_id: company_a
company_ids:
- company_a
groups_id:
- base.group_sale_salesman
- purchase.group_purchase_user
-
!record {model: res.users, id: res_users_company_b, view: False}:
name: User B
login: userb
password: userb
email: userb@yourcompany.com
company_id: company_b
company_ids:
- company_b
groups_id:
- base.group_sale_salesman
- purchase.group_purchase_user
-
Install COA for new companies.
-
!python {model: account.installer}: |
wiz_comp_a = self.create(cr, uid, {'charts': 'configurable', 'company_id': ref("company_a")})
self.execute(cr, uid, [wiz_comp_a])
wiz_comp_b = self.create(cr, uid, {'charts': 'configurable', 'company_id': ref("company_b")})
self.execute(cr, uid, [wiz_comp_b])
-
!python {model: wizard.multi.charts.accounts}: |
wiz_comp_a = self.create(cr, uid, {
'chart_template_id': '1',
'company_id': ref("company_a"),
'currency_id': ref("base.EUR"),
'code_digits': 6,
'purchase_tax': False,
'sale_tax': False})
self.execute(cr, uid, [wiz_comp_a])
wiz_comp_b = self.create(cr, uid, {
'chart_template_id': '1',
'company_id': ref("company_b"),
'currency_id': ref("base.EUR"),
'code_digits': 6,
'purchase_tax': False,
'sale_tax': False})
self.execute(cr, uid, [wiz_comp_b])
-
Now remove company from related partners of company and apply default accounts.
-
!python {model: res.partner}: |
account_obj = self.pool.get("account.account")
account_receivable_a = account_obj.search(cr, uid, [('company_id', '=', ref("company_a")), ('type', '=', 'receivable')], context=context)
account_payable_a = account_obj.search(cr, uid, [('company_id', '=', ref("company_a")), ('type', '=', 'payable')], context=context)
self.write(cr, uid, [ref("partner_company_a")], {
'company_id': False,
'property_account_receivable': account_receivable_a[0],
'property_account_payable': account_payable_a[0]
}, context=context)
account_receivable_b = account_obj.search(cr, uid, [('company_id', '=', ref("company_b")), ('type', '=', 'receivable')], context=context)
account_payable_b = account_obj.search(cr, uid, [('company_id', '=', ref("company_b")), ('type', '=', 'payable')], context=context)
self.write(cr, uid, [ref("partner_company_b")], {
'company_id': False,
'property_account_receivable': account_receivable_b[0],
'property_account_payable': account_payable_b[0]
}, context=context)
@@ -0,0 +1,24 @@
<?xml version="1.0"?>
<odoo>
<data>
<record model="ir.ui.view" id="view_company_inter_change_inherit_form">
<field name="name">res.company.form.inherit</field>
<field name="inherit_id" ref="base.view_company_form"/>
<field name="model">res.company</field>
<field name="arch" type="xml">
<xpath expr="//notebook" position="inside">
<page string="Inter-Company Rules">
<group string="Inter-Company Rules">
<field name="so_from_po" attrs="{'invisible': [('auto_generate_invoices', '=', True)]}"/>
<field name="po_from_so" attrs="{'invisible': [('auto_generate_invoices', '=', True)]}"/>
<field name="warehouse_id" attrs="{'required': [('po_from_so', '=', True)], 'invisible': [('po_from_so', '=', False)]}" domain="[('company_id', '=', active_id)]"/>
<field name="auto_validation" attrs="{'invisible': [('auto_generate_invoices', '=', True)]}"/>
<field name="auto_generate_invoices" attrs="{'invisible': ['|', ('so_from_po', '=', True), ('po_from_so', '=', True)]}"/>
<field name="intercompany_user_id" colspan="2" attrs="{'required': ['|','|',('so_from_po', '=', True), ('po_from_so', '=', True), ('auto_generate_invoices', '=', True)]}"/>
</group>
</page>
</xpath>
</field>
</record>
</data>
</odoo>
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="view_general_configuration_inter_company_rules" model="ir.ui.view">
<field name="name">base.config.settings.inter.company.rules</field>
<field name="model">base.config.settings</field>
<field name="inherit_id" ref="base_setup.view_general_configuration"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='module_inter_company_rules']" position='after'>
<div name="module_inter_company_rules_company_id" attrs="{'invisible': [('module_inter_company_rules', '=', False)]}">
<field name="company_id" widget="selection" class="oe_inline"/>
<field name="rule_type" widget="radio"/>
</div>
<div name="module_inter_company_rules_set_so_po" attrs="{'invisible': ['|', ('rule_type', '!=', 'so_and_po'), ('module_inter_company_rules', '=', False)]}">
<field name="so_from_po"/>
<label for="so_from_po"/>
</div>
<div name="module_inter_company_rules_set_po_so" attrs="{'invisible': ['|', ('rule_type', '!=', 'so_and_po'), ('module_inter_company_rules', '=', False)]}">
<field name="po_from_so"/>
<label for="po_from_so"/>
</div>
<div name="module_inter_company_rules_warehouse_id" attrs="{'invisible': ['|', ('po_from_so', '=', False), ('module_inter_company_rules', '=', False)]}">
<label for="warehouse_id"/>
<field name="warehouse_id" class="oe_inline" attrs="{'required': [('po_from_so', '=', True), ('module_inter_company_rules', '=', True)]}" domain="[('company_id', '=', company_id)]"/>
</div>
<div name="module_inter_company_rules_set_auto_validation" attrs="{'invisible': ['|', ('rule_type', '!=', 'so_and_po'), ('module_inter_company_rules', '=', False)]}">
<field name="auto_validation"/>
<label for="auto_validation"/>
</div>
</xpath>
</field>
</record>
</data>
</odoo>