Merge lp:~openerp-dev/openobject-addons/trunk-intercompany-rules-tpa into lp:openobject-addons

Proposed by Turkesh Patel (openERP)
Status: Needs review
Proposed branch: lp:~openerp-dev/openobject-addons/trunk-intercompany-rules-tpa
Merge into: lp:openobject-addons
Diff against target: 1119 lines (+1034/-2)
13 files modified
base_setup/res_config.py (+2/-2)
base_setup/res_config_view.xml (+9/-0)
inter_company_rules/__init__.py (+25/-0)
inter_company_rules/__openerp__.py (+48/-0)
inter_company_rules/inter_company_invoice.py (+140/-0)
inter_company_rules/inter_company_so_po.py (+348/-0)
inter_company_rules/inter_company_so_po_view.xml (+35/-0)
inter_company_rules/res_config.py (+81/-0)
inter_company_rules/res_config_view.xml (+34/-0)
inter_company_rules/test/inter_company_invoice.yml (+79/-0)
inter_company_rules/test/inter_company_po_to_so.yml (+42/-0)
inter_company_rules/test/inter_company_so_to_po.yml (+46/-0)
inter_company_rules/test/test_intercompany_data.yml (+145/-0)
To merge this branch: bzr merge lp:~openerp-dev/openobject-addons/trunk-intercompany-rules-tpa
Reviewer Review Type Date Requested Status
OpenERP Core Team Pending
Review via email: mp+217421@code.launchpad.net

Description of the change

--> 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.

To post a comment you must log in.

Unmerged revisions

9025. By Turkesh Patel (openERP)

[MRG] merge with lp:openobject-addons

9024. By Turkesh Patel (openERP)

[IMP] improved uid to confirm order on auto validation.

9023. By Pinakin Nayi (OpenERP)

[Merge]merge with main

9022. By Turkesh Patel (openERP)

[MRG] merge with lp:openobject-addons

9021. By Sunil Sharma(OpenERP)

[imp]:improve invoice yml

9020. By Turkesh Patel (openERP)

[FIX] improved yml test case product.

9019. By Turkesh Patel (openERP)

[MRG] merge with lp:openobject-addons

9018. By Turkesh Patel (openERP)

[MRG] merge with lp:openobject-addons

9017. By Turkesh Patel (openERP)

[IMP] improved view

9016. By Turkesh Patel (openERP)

[IMP] remove use of netsvc.LocalService as it have been depricated.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'base_setup/res_config.py'
2--- base_setup/res_config.py 2014-02-17 13:48:19 +0000
3+++ base_setup/res_config.py 2014-04-28 12:01:12 +0000
4@@ -43,7 +43,7 @@
5 help="""This installs the module google_calendar."""),
6 'font': fields.many2one('res.font', string="Report Font", domain=[('mode', 'in', ('Normal', 'Regular', 'all', 'Book'))],
7 help="Set the font into the report header, it will be used as default font in the RML reports of the user company"),
8-
9+ 'module_inter_company_rules': fields.boolean('Manage Inter Company', help="""This installs the module inter_company_rules."""),
10 }
11
12 _defaults= {
13@@ -108,4 +108,4 @@
14 help='Get access to statistics with your mass mailing, manage campaigns.'),
15 }
16
17-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
18\ No newline at end of file
19+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
20
21=== modified file 'base_setup/res_config_view.xml'
22--- base_setup/res_config_view.xml 2014-02-17 13:48:19 +0000
23+++ base_setup/res_config_view.xml 2014-04-28 12:01:12 +0000
24@@ -104,6 +104,15 @@
25 </div>
26 </div>
27 </group>
28+ <group>
29+ <label for="module_inter_company_rules" string="Inter company"/>
30+ <div name="inter_company">
31+ <div name="module_inter_company_rules">
32+ <field name="module_inter_company_rules" class="oe_inline"/>
33+ <label for="module_inter_company_rules"/>
34+ </div>
35+ </div>
36+ </group>
37 </form>
38 </field>
39 </record>
40
41=== added directory 'inter_company_rules'
42=== added file 'inter_company_rules/__init__.py'
43--- inter_company_rules/__init__.py 1970-01-01 00:00:00 +0000
44+++ inter_company_rules/__init__.py 2014-04-28 12:01:12 +0000
45@@ -0,0 +1,25 @@
46+# -*- coding: utf-8 -*-
47+##############################################################################
48+#
49+# OpenERP, Open Source Management Solution
50+# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
51+#
52+# This program is free software: you can redistribute it and/or modify
53+# it under the terms of the GNU Affero General Public License as
54+# published by the Free Software Foundation, either version 3 of the
55+# License, or (at your option) any later version.
56+#
57+# This program is distributed in the hope that it will be useful,
58+# but WITHOUT ANY WARRANTY; without even the implied warranty of
59+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
60+# GNU Affero General Public License for more details.
61+#
62+# You should have received a copy of the GNU Affero General Public License
63+# along with this program. If not, see <http://www.gnu.org/licenses/>.
64+#
65+##############################################################################
66+
67+import inter_company_so_po
68+import inter_company_invoice
69+import res_config
70+
71
72=== added file 'inter_company_rules/__openerp__.py'
73--- inter_company_rules/__openerp__.py 1970-01-01 00:00:00 +0000
74+++ inter_company_rules/__openerp__.py 2014-04-28 12:01:12 +0000
75@@ -0,0 +1,48 @@
76+# -*- coding: utf-8 -*-
77+##############################################################################
78+#
79+# OpenERP, Open Source Management Solution
80+# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
81+#
82+# This program is free software: you can redistribute it and/or modify
83+# it under the terms of the GNU Affero General Public License as
84+# published by the Free Software Foundation, either version 3 of the
85+# License, or (at your option) any later version.
86+#
87+# This program is distributed in the hope that it will be useful,
88+# but WITHOUT ANY WARRANTY; without even the implied warranty of
89+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
90+# GNU Affero General Public License for more details.
91+#
92+# You should have received a copy of the GNU Affero General Public License
93+# along with this program. If not, see <http://www.gnu.org/licenses/>.
94+#
95+##############################################################################
96+
97+{
98+ 'name': 'Inter Company Module for Sale/Purchase Orders and Invoices',
99+ 'version': '1.1',
100+ '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.
101+
102+ Supported documents are SO, PO and invoices/refunds.
103+''',
104+ 'author': 'OpenERP SA',
105+ 'website': 'http://openerp.com',
106+ 'depends': ['sale', 'purchase', 'sale_stock', 'sale_order_dates'],
107+ 'data': [
108+ 'inter_company_so_po_view.xml',
109+ 'res_config_view.xml',
110+ ],
111+ 'test': [
112+ 'test/test_intercompany_data.yml',
113+ 'test/inter_company_so_to_po.yml',
114+ 'test/inter_company_po_to_so.yml',
115+ 'test/inter_company_invoice.yml'
116+ ],
117+ 'demo': [],
118+ 'installable': True,
119+ 'application': True,
120+ 'auto_install': False,
121+}
122+
123+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
124
125=== added file 'inter_company_rules/inter_company_invoice.py'
126--- inter_company_rules/inter_company_invoice.py 1970-01-01 00:00:00 +0000
127+++ inter_company_rules/inter_company_invoice.py 2014-04-28 12:01:12 +0000
128@@ -0,0 +1,140 @@
129+# -*- coding: utf-8 -*-
130+##############################################################################
131+#
132+# OpenERP, Open Source Management Solution
133+# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
134+#
135+# This program is free software: you can redistribute it and/or modify
136+# it under the terms of the GNU Affero General Public License as
137+# published by the Free Software Foundation, either version 3 of the
138+# License, or (at your option) any later version.
139+#
140+# This program is distributed in the hope that it will be useful,
141+# but WITHOUT ANY WARRANTY; without even the implied warranty of
142+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
143+# GNU Affero General Public License for more details.
144+#
145+# You should have received a copy of the GNU Affero General Public License
146+# along with this program. If not, see <http://www.gnu.org/licenses/>.
147+#
148+##############################################################################
149+
150+from openerp.osv import osv, fields
151+from openerp.tools.translate import _
152+
153+#Invoice Directions.
154+class account_invoice(osv.osv):
155+ _inherit = 'account.invoice'
156+
157+ _columns = {
158+ 'auto_generated': fields.boolean('Auto Generated Document'),
159+ 'auto_invoice_id': fields.many2one('account.invoice', 'Source Invoice', readonly=True),
160+ }
161+
162+ def copy(self, cr, uid, id, default=None, context=None):
163+ if default is None:
164+ default = {}
165+ default.update({'auto_invoice_id': False, 'auto_generated': False})
166+ return super(account_invoice, self).copy(cr, uid, id, default=default, context=context)
167+
168+ def invoice_validate(self, cr, uid, ids, context=None):
169+ """
170+ Validated invoice generate cross invoice base on company rules.
171+ """
172+ company_obj = self.pool.get('res.company')
173+ for invoice in self.browse(cr, uid, ids, context=context):
174+ #do not consider invoices that have already been auto-generated, nor the invoices that were already validated in the past
175+ company = company_obj._find_company_from_partner(cr, uid, invoice.partner_id.id, context=context)
176+ if company and company.auto_generate_invoices and not invoice.auto_generated:
177+ if invoice.type == 'out_invoice':
178+ self.action_create_invoice(cr, uid, invoice, company, 'in_invoice', 'purchase', context=context)
179+ elif invoice.type == 'in_invoice':
180+ self.action_create_invoice(cr, uid, invoice, company, 'out_invoice', 'sale', context=context)
181+ elif invoice.type == 'out_refund':
182+ self.action_create_invoice(cr, uid, invoice, company, 'in_refund', 'purchase_refund', context=context)
183+ elif invoice.type == 'in_refund':
184+ self.action_create_invoice(cr, uid, invoice, company, 'out_refund', 'sale_refund', context=context)
185+ return super(account_invoice, self).invoice_validate(cr, uid, ids, context=context)
186+
187+ def action_create_invoice(self, cr, uid, invoice, company, inv_type, journal_type, context=None):
188+ if context is None:
189+ context = {}
190+ inv_obj = self.pool.get('account.invoice')
191+ inv_line_obj = self.pool.get('account.invoice.line')
192+
193+ #Find user for creating the invoice from company
194+ intercompany_uid = company.intercompany_user_id and company.intercompany_user_id.id or False
195+ if not intercompany_uid:
196+ raise osv.except_osv(_('Warning!'), _('Provide one user for intercompany relation for % ') % company.name)
197+
198+ #
199+ ctx = context.copy()
200+ ctx['force_company'] = company.id
201+ this_company_partner = invoice.company_id.partner_id
202+ inv_lines = []
203+ for line in invoice.invoice_line:
204+ #To find lot of data from product onchanges because its already avail method in core.
205+ product_uom = line.product_id.uom_id and line.product_id.uom_id.id or False
206+ line_data = inv_line_obj.product_id_change(cr, intercompany_uid, [line.id], line.product_id.id, product_uom, qty=line.quantity, name='', type=inv_type, partner_id=this_company_partner.id, fposition_id=this_company_partner.property_account_position.id, context=ctx, company_id=company.id)
207+ inv_line_data = self._prepare_inv_line(cr, uid, line_data, line, context=ctx)
208+ inv_line_id = inv_line_obj.create(cr, intercompany_uid, inv_line_data, context=ctx)
209+ inv_lines.append(inv_line_id)
210+
211+ #create invoice
212+ invoice_vals = self._prepare_inv(cr, intercompany_uid, invoice, inv_lines, inv_type, journal_type, company, context=ctx)
213+ return inv_obj.create(cr, intercompany_uid, invoice_vals, context=ctx)
214+
215+ def _prepare_inv_line(self, cr, uid, line_data, line, context=None):
216+ """ Generate invoice line dictionary"""
217+ vals = {
218+ 'name': line.name,
219+ 'price_unit': line.price_unit,
220+ 'quantity': line.quantity,
221+ 'discount': line.discount,
222+ 'product_id': line.product_id.id or False,
223+ 'uos_id': line.uos_id.id or False,
224+ 'sequence': line.sequence,
225+ 'invoice_line_tax_id': [(6, 0, line_data['value'].get('invoice_line_tax_id', []))],
226+ 'account_analytic_id': line.account_analytic_id.id or False,
227+ }
228+ if line_data['value'].get('account_id', False):
229+ vals['account_id'] = line_data['value']['account_id']
230+ return vals
231+
232+ def _prepare_inv(self, cr, uid, invoice, inv_lines, inv_type, jrnl_type, company, context=None):
233+ """ Generate invoice dictionary """
234+ context = context or {}
235+ journal_obj = self.pool.get('account.journal')
236+ period_obj = self.pool.get('account.period')
237+
238+ #To find journal.
239+ journal_ids = journal_obj.search(cr, uid, [('type', '=', jrnl_type), ('company_id', '=', company.id)], limit=1)
240+ if not journal_ids:
241+ raise osv.except_osv(_('Error!'),
242+ _('Please define %s journal for this company: "%s" (id:%d).') % (jrnl_type, company.name, company.id))
243+ #To find periods of supplier company.
244+ ctx = context.copy()
245+ ctx.update(company_id=company.id)
246+ period_ids = period_obj.find(cr, uid, invoice.date_invoice, context=ctx)
247+ #To find account,payment term,fiscal position,bank.
248+ partner_data = self.onchange_partner_id(cr, uid, [invoice.id], inv_type, invoice.company_id.partner_id.id, company_id=company.id)
249+
250+ return {
251+ 'name': invoice.name,
252+ 'origin': invoice.company_id.name + _(' Invoice: ') + str(invoice.number),
253+ 'type': inv_type,
254+ 'date_invoice': invoice.date_invoice,
255+ 'reference': invoice.reference,
256+ 'account_id': partner_data['value'].get('account_id', False),
257+ 'partner_id': invoice.company_id.partner_id.id,
258+ 'journal_id': journal_ids[0],
259+ 'invoice_line': [(6, 0, inv_lines)],
260+ 'currency_id': invoice.currency_id and invoice.currency_id.id,
261+ 'fiscal_position': partner_data['value'].get('fiscal_position', False),
262+ 'payment_term': partner_data['value'].get('payment_term', False),
263+ 'company_id': company.id,
264+ 'period_id': period_ids and period_ids[0] or False,
265+ 'partner_bank_id': partner_data['value'].get('partner_bank_id', False),
266+ 'auto_generated': True,
267+ 'auto_invoice_id': invoice.id,
268+ }
269
270=== added file 'inter_company_rules/inter_company_so_po.py'
271--- inter_company_rules/inter_company_so_po.py 1970-01-01 00:00:00 +0000
272+++ inter_company_rules/inter_company_so_po.py 2014-04-28 12:01:12 +0000
273@@ -0,0 +1,348 @@
274+# -*- coding: utf-8 -*-
275+##############################################################################
276+#
277+# OpenERP, Open Source Management Solution
278+# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
279+#
280+# This program is free software: you can redistribute it and/or modify
281+# it under the terms of the GNU Affero General Public License as
282+# published by the Free Software Foundation, either version 3 of the
283+# License, or (at your option) any later version.
284+#
285+# This program is distributed in the hope that it will be useful,
286+# but WITHOUT ANY WARRANTY; without even the implied warranty of
287+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
288+# GNU Affero General Public License for more details.
289+#
290+# You should have received a copy of the GNU Affero General Public License
291+# along with this program. If not, see <http://www.gnu.org/licenses/>.
292+#
293+##############################################################################
294+
295+from openerp.osv import osv
296+from openerp.osv import fields
297+from openerp.tools.translate import _
298+from openerp import SUPERUSER_ID
299+
300+class res_company(osv.osv):
301+ _inherit = 'res.company'
302+
303+ _columns = {
304+ 'so_from_po': fields.boolean("Create Sale Orders when buying to this company", help='Generate a Sale Order when a Purchase Order with this company as supplier is created.'),
305+ 'po_from_so': fields.boolean("Create Purchase Orders when selling to this company", help='Generate a Purchase Order when a Sale Order with this company as customer is created.'),
306+ 'auto_generate_invoices': fields.boolean("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."),
307+ 'auto_validation': fields.boolean('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"),
308+ 'intercompany_user_id': fields.many2one('res.users', 'Inter Company User', help="Responsible user for creation of documents triggered by intercompany rules."),
309+ 'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse For Purchase Orders', help="Default value to set on Purchase Orders that will be created based on Sale Orders made to this company")
310+ }
311+
312+ _defaults = {
313+ 'intercompany_user_id': SUPERUSER_ID,
314+ }
315+
316+ def _find_company_from_partner(self, cr, uid, partner_id, context=None):
317+ """ @Return : company_id"""
318+ company_ids = self.search(cr, SUPERUSER_ID, [('partner_id', '=', partner_id)], context=context)
319+ if company_ids:
320+ return self.browse(cr, SUPERUSER_ID, company_ids[0], context=context)
321+
322+ def _check_intercompany_missmatch_selection(self, cr, uid, ids, context=None):
323+ for company in self.browse(cr, uid, ids, context=context):
324+ if (company.po_from_so or company.so_from_po) and company.auto_generate_invoices:
325+ raise osv.except_osv(_('Invalid Action!'), _("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')!"))
326+ return True
327+
328+ _constraints = [
329+ (_check_intercompany_missmatch_selection, 'Invalid Action: Cannot Select group selection for intercompany', []),
330+ ]
331+
332+class sale_order(osv.osv):
333+ _inherit = "sale.order"
334+ _columns = {
335+ 'auto_generated': fields.boolean('Auto Generated Sale Order'),
336+ 'auto_po_id': fields.many2one('purchase.order', 'Source Purchase Order', readonly=True),
337+ }
338+
339+ def copy(self, cr, uid, id, default=None, context=None):
340+ if default is None:
341+ default = {}
342+ default.update({'auto_po_id': False, 'auto_generated': False})
343+ return super(sale_order, self).copy(cr, uid, id, default=default, context=context)
344+
345+ def action_button_confirm(self, cr, uid, ids, context=None):
346+ """ Overwrite method also generate intercompany purchase order base on conditions."""
347+ company_obj = self.pool.get('res.company')
348+
349+ res = super(sale_order, self).action_button_confirm(cr, uid, ids, context=context)
350+
351+ for order in self.browse(cr, uid, ids, context=context):
352+ #If company_id not found, return to normal behavior
353+ if not order.company_id:
354+ continue
355+ #Total check for intersale relation.
356+ self._check_amount_total(cr, uid, order.id, context=context)
357+
358+ company_rec = company_obj._find_company_from_partner(cr, uid, order.partner_id.id, context=context)
359+ if company_rec and company_rec.po_from_so and (not order.auto_generated):
360+ self.action_create_po(cr, uid, ids, order.id, company_rec, context=context)
361+ return res
362+
363+ def _po_line_vals(self, cr, uid, line, company_partner, date_order, purchase_id, company, context=None):
364+ """ @ return : Purchase Line values dictionary """
365+ line_obj = self.pool.get('purchase.order.line')
366+ tax_obj = self.pool.get('account.tax')
367+
368+ #price on PO line should be line - discount
369+ price = line.price_unit - (line.price_unit * (line.discount / 100))
370+
371+ #Computing Default taxes of lines. It may not affect because of parallel company relation
372+ taxes_ids = [x.id for x in line.tax_id]
373+ if line.product_id:
374+ onchange_lines = line_obj.onchange_product_id(cr, uid, [], False, line.product_id and line.product_id.id or False, line.product_uom_qty, line.product_id and line.product_id.uom_po_id.id or False, company_partner.id, context=context)
375+ if onchange_lines.get('value') and onchange_lines['value'].get('taxes_id'):
376+ taxes_ids = onchange_lines['value']['taxes_id']
377+ #Fetch taxes by company not by inter-company user
378+ cmpny_wise_taxes = []
379+ for tx_rec in tax_obj.browse(cr, SUPERUSER_ID, taxes_ids, context=context):
380+ if tx_rec.company_id.id == company.id:
381+ cmpny_wise_taxes.append(tx_rec.id)
382+
383+ return {
384+ 'name': line.name,
385+ 'order_id': purchase_id,
386+ 'product_qty': line.product_uom_qty,
387+ 'product_id': line.product_id and line.product_id.id or False,
388+ 'product_uom': line.product_id and line.product_id.uom_po_id.id or line.product_uom.id,
389+ 'price_unit': price or 0.0,
390+ 'company_id': line.order_id.company_id.id,
391+ 'date_planned': line.order_id.commitment_date or date_order,
392+ 'taxes_id': [(6, 0, cmpny_wise_taxes)],
393+ }
394+
395+ def _po_vals(self, cr, uid, sale, company, this_company_partner, context=None):
396+ """ @ return : Purchase values dictionary """
397+ context = context or {}
398+ seq_obj = self.pool.get('ir.sequence')
399+ warehouse_obj = self.pool.get('stock.warehouse')
400+
401+ #To find location and warehouse,pick warehouse from company object
402+ warehouse_id = company.warehouse_id and company.warehouse_id.company_id.id == company.id and company.warehouse_id.id or False
403+ if not warehouse_id:
404+ raise osv.except_osv(_('Invalid Action!'), _('Configure correct warehouse for company(%s) from Menu: Settings/companies/companies' % (company.name)))
405+ location_id = warehouse_obj.browse(cr, SUPERUSER_ID, warehouse_id, context=context).lot_stock_id.id
406+ pricelist_id = this_company_partner.property_product_pricelist_purchase.id
407+ return {
408+ 'name': seq_obj.get(cr, SUPERUSER_ID, 'purchase.order'),
409+ 'origin': sale.name,
410+ 'partner_id': this_company_partner.id,
411+ 'warehouse_id': warehouse_id,
412+ 'location_id': location_id,
413+ 'pricelist_id': pricelist_id,
414+ 'date_order': sale.date_order,
415+ 'company_id': company.id,
416+ 'fiscal_position': this_company_partner.property_account_position or False,
417+ 'payment_term_id': this_company_partner.property_supplier_payment_term.id or False,
418+ 'auto_generated': True,
419+ 'auto_so_id': sale.id,
420+ 'partner_ref': sale.name,
421+ 'dest_address_id': sale.partner_shipping_id and sale.partner_shipping_id.id or False,
422+ }
423+
424+ def action_create_po(self, cr, uid, ids, sale_id, company, context=None):
425+ """ Intercompany Purchase Order trigger when sale order confirm"""
426+ if context is None:
427+ context = {}
428+
429+ purchase_obj = self.pool.get('purchase.order')
430+ purchaseline_obj = self.pool.get('purchase.order.line')
431+
432+ sale = self.browse(cr, SUPERUSER_ID, sale_id, context=context)
433+ this_company_partner = sale.company_id and sale.company_id.partner_id or False
434+ if not company or not this_company_partner.id:
435+ return
436+
437+ #Find user for creating and validating SO/PO from company
438+ update_uid = company.intercompany_user_id and company.intercompany_user_id.id or False
439+ if not update_uid:
440+ raise osv.except_osv(_('Warning!'), _('Provide one user for intercompany relation for % ') % company.name)
441+
442+ if not purchase_obj.check_access_rights(cr, update_uid, 'create', raise_exception=False):
443+ raise osv.except_osv(_('Access Rights!'), _("Inter company user of company %s doesn't have enough access rights") % company.name)
444+
445+ #Check pricelist currency should be same with SO/PO document
446+ if sale.pricelist_id.currency_id.id != this_company_partner.property_product_pricelist_purchase.currency_id.id:
447+ raise osv.except_osv(_('Different Currency!'), _('You cannot create PO from SO because purchase pricelist currency is different than sale pricelist currency.'))
448+
449+ #create the PO
450+ po_vals = self._po_vals(cr, update_uid, sale, company, this_company_partner, context=context)
451+ purchase_id = purchase_obj.create(cr, update_uid, po_vals, context=context)
452+ for line in sale.order_line:
453+ po_line_vals = self._po_line_vals(cr, update_uid, line, this_company_partner, sale.date_order, purchase_id, company, context=context)
454+ purchaseline_obj.create(cr, update_uid, po_line_vals, context=context)
455+
456+ #write customer reference field on SO
457+ if not sale.client_order_ref:
458+ self.write(cr, uid, sale.id, {'client_order_ref': purchase_obj.browse(cr, SUPERUSER_ID, purchase_id).name}, context=context)
459+
460+ #auto-validate the purchase order if needed
461+ if company.auto_validation:
462+ purchase_obj.signal_purchase_confirm(cr, update_uid, [purchase_id])
463+ return True
464+
465+ def _check_amount_total(self, cr, uid, sale_id, context=None):
466+ """ Check If total amount missmatch then raise the warning."""
467+ context = context or {}
468+ purchase_obj = self.pool.get('purchase.order')
469+ sale = self.browse(cr, SUPERUSER_ID, sale_id, context=context)
470+ #Total check for intersale relation.
471+ if sale.auto_po_id:
472+ amount_total = purchase_obj.browse(cr, SUPERUSER_ID, sale.auto_po_id.id, context=context).amount_total
473+ if sale.amount_total != amount_total:
474+ raise osv.except_osv(_('Total Mismatch!'), _('You cannot confirm this SO because its total amount does not match the total amount of the PO it is coming from.'))
475+ return True
476+
477+sale_order()
478+
479+class purchase_order(osv.osv):
480+ _inherit = "purchase.order"
481+ _columns = {
482+ 'auto_generated': fields.boolean('Auto Generated Purchase Order'),
483+ 'auto_so_id': fields.many2one('sale.order', 'Source Sale Order', readonly=True)
484+ }
485+
486+ def copy(self, cr, uid, id, default=None, context=None):
487+ if default is None:
488+ default = {}
489+ default.update({'auto_so_id': False, 'auto_generated': False})
490+ return super(purchase_order, self).copy(cr, uid, id, default=default, context=context)
491+
492+ def wkf_confirm_order(self, cr, uid, ids, context=None):
493+ """ Overwrite method also generate intercompany sale order base on conditions."""
494+ company_obj = self.pool.get('res.company')
495+
496+ res = super(purchase_order, self).wkf_confirm_order(cr, uid, ids, context=context)
497+ for order in self.browse(cr, uid, ids, context):
498+ #Price check for intersale relation.
499+ self._check_amount_total(cr, uid, order.id, context=context)
500+
501+ #get the company from partner then trigger action of intercompany relation.
502+ company_rec = company_obj._find_company_from_partner(cr, uid, order.partner_id.id, context=context)
503+ if company_rec and company_rec.so_from_po and (not order.auto_generated):
504+ self.action_create_so(cr, uid, order.id, company_rec, context=context)
505+ return res
506+
507+ def _so_line_vals(self, cr, uid, line, partner, company, sale_id, context=None):
508+ """ @ return : Sale Line values dictionary """
509+ context = context or {}
510+ saleline_obj = self.pool.get('sale.order.line')
511+ tax_obj = self.pool.get('account.tax')
512+
513+ #It may not affected because of parallel company relation
514+ taxes_ids = [x.id for x in line.taxes_id]
515+ price = line.price_unit or 0.0
516+ if line.product_id:
517+ soline_onchange = saleline_obj.product_id_change(cr, uid, [], False, line.product_id.id, qty=line.product_qty,
518+ uom=line.product_id.uom_id.id, partner_id=partner.id, context=context)
519+ if soline_onchange.get('value') and soline_onchange['value'].get('tax_id'):
520+ taxes_ids = soline_onchange['value']['tax_id']
521+
522+ #Fetch taxes by company not by inter-company user
523+ cmpny_wise_taxes = []
524+ for tx_rec in tax_obj.browse(cr, SUPERUSER_ID, taxes_ids, context=context):
525+ if tx_rec.company_id.id == company.id:
526+ cmpny_wise_taxes.append(tx_rec.id)
527+
528+ return {
529+ 'name': line.product_id and line.product_id.name or line.name,
530+ 'order_id': sale_id,
531+ 'product_uom_qty': line.product_qty,
532+ 'product_id': line.product_id and line.product_id.id or False,
533+ 'product_uom': line.product_id and line.product_id.uom_id.id or line.product_uom.id,
534+ 'price_unit': price,
535+ 'delay': line.product_id and line.product_id.sale_delay or 0.0,
536+ 'company_id': company.id,
537+ 'tax_id': [(6, 0, cmpny_wise_taxes)],
538+ }
539+
540+ def _so_vals(self, cr, uid, name, purchase_id, partner, company, direct_delivery_address, context=None):
541+ """ @ return : Sale values dictionary """
542+ context = context or {}
543+ seq_obj = self.pool.get('ir.sequence')
544+ partner_obj = self.pool.get('res.partner')
545+ partner_addr = partner_obj.address_get(cr, SUPERUSER_ID, [partner.id], ['default', 'invoice', 'delivery', 'contact'])
546+ pricelist_id = partner.property_product_pricelist.id
547+ fpos = partner.property_account_position and partner.property_account_position.id or False
548+ #Not good but browse here for compatible code
549+ return {
550+ 'name': seq_obj.get(cr, SUPERUSER_ID, 'sale.order') or '/',
551+ 'company_id': company.id,
552+ 'client_order_ref': name,
553+ 'partner_id': partner.id,
554+ 'pricelist_id': pricelist_id,
555+ 'partner_invoice_id': partner_addr['invoice'],
556+ 'date_order': fields.date.context_today(self, cr, uid, context=context),
557+ 'fiscal_position': fpos,
558+ 'user_id': False,
559+ 'auto_generated': True,
560+ 'auto_po_id': purchase_id,
561+ 'partner_shipping_id': direct_delivery_address or partner_addr['delivery']
562+ }
563+
564+ def action_create_so(self, cr, uid, order_id, company, context=None):
565+ if context is None:
566+ context = {}
567+
568+ sale_obj = self.pool.get('sale.order')
569+ saleline_obj = self.pool.get('sale.order.line')
570+
571+ pur = self.browse(cr, SUPERUSER_ID, order_id)
572+ this_company_partner = pur.company_id.partner_id
573+
574+ #To find user for creating and validation SO/PO from partner company
575+ update_uid = company.intercompany_user_id and company.intercompany_user_id.id or False
576+ if not update_uid:
577+ raise osv.except_osv(_('Warning!'), _('Provide atleast one user for intercompany relation for % ') % company.name)
578+
579+ if not sale_obj.check_access_rights(cr, update_uid, 'create', raise_exception=False):
580+ raise osv.except_osv(_('Access Rights!'), _("Inter company user of company %s doesn't have enough access rights") % company.name)
581+
582+ #Check pricelist currency should be same with SO/PO document
583+ if pur.pricelist_id.currency_id.id != this_company_partner.property_product_pricelist.currency_id.id:
584+ raise osv.except_osv(_('Different Currency!'), _('You cannot create SO from PO because sale pricelist currency is different than purchase pricelist currency.'))
585+
586+ #create the SO
587+ direct_delivery_address = pur.dest_address_id and pur.dest_address_id.id or False
588+ so_vals = self._so_vals(cr, update_uid, pur.name, pur.id, this_company_partner, company, direct_delivery_address, context=context)
589+ sale_id = sale_obj.create(cr, update_uid, so_vals, context=context)
590+ for line in pur.order_line:
591+ so_line_vals = self._so_line_vals(cr, update_uid, line, this_company_partner, company, sale_id, context=context)
592+ saleline_obj.create(cr, update_uid, so_line_vals, context=context)
593+
594+ #write supplier reference field on PO
595+ if not pur.partner_ref:
596+ self.write(cr, uid, pur.id, {'partner_ref': sale_obj.browse(cr, SUPERUSER_ID, sale_id).name}, context=context)
597+
598+ #Validation of sale order
599+ if company.auto_validation:
600+ sale_obj.signal_order_confirm(cr, update_uid, [sale_id])
601+ return True
602+
603+ def _check_amount_total(self, cr, uid, purchase_id, context=None):
604+ """ Check If total amount missmatch then raise the warning."""
605+ context = context or {}
606+ sale_obj = self.pool.get('sale.order')
607+ purchase = self.browse(cr, SUPERUSER_ID, purchase_id, context=context)
608+ #Total check for intersale relation.
609+ if purchase.auto_so_id:
610+ amount_total = sale_obj.browse(cr, SUPERUSER_ID, purchase.auto_so_id.id, context=context).amount_total
611+ if purchase.amount_total != amount_total:
612+ raise osv.except_osv(_('Total Mismatch!'), _('You cannot confirm this PO because its total amount does not match the total amount of the SO is it coming from.'))
613+ return True
614+
615+ def onchange_company_id(self, cr, uid, ids, company_id, context=None):
616+ warehouse_id = False
617+ if company_id:
618+ warehouse_id = self.pool.get('res.company').browse(cr, uid, company_id, context=None).warehouse_id.id
619+ return {'value': {'warehouse_id': warehouse_id }}
620+
621+purchase_order()
622
623=== added file 'inter_company_rules/inter_company_so_po_view.xml'
624--- inter_company_rules/inter_company_so_po_view.xml 1970-01-01 00:00:00 +0000
625+++ inter_company_rules/inter_company_so_po_view.xml 2014-04-28 12:01:12 +0000
626@@ -0,0 +1,35 @@
627+<?xml version="1.0"?>
628+<openerp>
629+ <data>
630+
631+ <record model="ir.ui.view" id="view_company_inter_change_inherit_form">
632+ <field name="name">res.company.form.inherit</field>
633+ <field name="inherit_id" ref="base.view_company_form"/>
634+ <field name="model">res.company</field>
635+ <field name="arch" type="xml">
636+ <xpath expr="//notebook/page[@string='Configuration']" position="inside">
637+ <group string="Inter-Company Rules">
638+ <field name="so_from_po" attrs="{'invisible': [('auto_generate_invoices', '=', True)]}"/>
639+ <field name="po_from_so" attrs="{'invisible': [('auto_generate_invoices', '=', True)]}"/>
640+ <field name="warehouse_id" attrs="{'required': [('po_from_so', '=', True)], 'invisible': [('po_from_so', '=', False)]}" domain="[('company_id', '=', active_id)]"/>
641+ <field name="auto_validation" attrs="{'invisible': [('auto_generate_invoices', '=', True)]}"/>
642+ <field name="auto_generate_invoices" attrs="{'invisible': ['|', ('so_from_po', '=', True), ('po_from_so', '=', True)]}"/>
643+ <field name="intercompany_user_id" colspan="2" attrs="{'required': ['|','|',('so_from_po', '=', True), ('po_from_so', '=', True), ('auto_generate_invoices', '=', True)]}"/>
644+ </group>
645+ </xpath>
646+ </field>
647+ </record>
648+
649+ <record model="ir.ui.view" id="view_purchase_order_form_inherit">
650+ <field name="name">purchase.purchase_order_form.inherit</field>
651+ <field name="inherit_id" ref="purchase.purchase_order_form"/>
652+ <field name="model">purchase.order</field>
653+ <field name="arch" type="xml">
654+ <xpath expr="//field[@name='company_id']" position="replace">
655+ <field name="company_id" on_change="onchange_company_id(company_id)" groups="base.group_multi_company" widget="selection"/>
656+ </xpath>
657+ </field>
658+ </record>
659+
660+ </data>
661+</openerp>
662
663=== added file 'inter_company_rules/res_config.py'
664--- inter_company_rules/res_config.py 1970-01-01 00:00:00 +0000
665+++ inter_company_rules/res_config.py 2014-04-28 12:01:12 +0000
666@@ -0,0 +1,81 @@
667+# -*- coding: utf-8 -*-
668+##############################################################################
669+#
670+# OpenERP, Open Source Management Solution
671+# Copyright (C) 2012-Today OpenERP SA (<http://www.openerp.com>)
672+#
673+# This program is free software: you can redistribute it and/or modify
674+# it under the terms of the GNU General Public License as published by
675+# the Free Software Foundation, either version 3 of the License, or
676+# (at your option) any later version.
677+#
678+# This program is distributed in the hope that it will be useful,
679+# but WITHOUT ANY WARRANTY; without even the implied warranty of
680+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
681+# GNU General Public License for more details.
682+#
683+# You should have received a copy of the GNU General Public License
684+# along with this program. If not, see <http://www.gnu.org/licenses/>
685+#
686+##############################################################################
687+from openerp.osv import osv, fields
688+
689+class inter_company_rules_configuration(osv.TransientModel):
690+ _inherit = 'base.config.settings'
691+
692+ _columns = {
693+ 'company_id': fields.many2one('res.company', string="Select Company", help="Select company to setup Inter company rules."),
694+ 'set_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."),
695+ 'so_from_po': fields.boolean("Create Sale Orders when buying to this company", help='Generate a Sale Order when a Purchase Order with this company as supplier is created.'),
696+ 'po_from_so': fields.boolean("Create Purchase Orders when selling to this company", help='Generate a Purchase Order when a Sale Order with this company as customer is created.'),
697+ 'auto_validation': fields.boolean('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."),
698+ 'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse For Purchase Orders', help="Default value to set on Purchase Orders that will be created based on Sale Orders made to this company.")
699+ }
700+
701+ _defaults= {
702+ 'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'base.config.settings', context=c),
703+ }
704+
705+ def onchange_set_type(self, cr, uid, ids, set_type, context=None):
706+ values = {}
707+ if set_type == 'invoice_and_refunds':
708+ values.update({
709+ 'so_from_po': False,
710+ 'po_from_so': False,
711+ 'auto_validation' : False
712+ })
713+ elif set_type == 'so_and_po':
714+ values['invoice_and_refunds'] = False
715+ return {'value': values}
716+
717+ def onchange_company_id(self, cr, uid, ids, company_id, context=None):
718+ values = {}
719+ if company_id:
720+ company = self.pool.get('res.company').browse(cr, uid, company_id, context=context)
721+ set_type = False
722+ if company.so_from_po or company.po_from_so or company.auto_validation:
723+ set_type = 'so_and_po'
724+ elif company.auto_generate_invoices:
725+ set_type = 'invoice_and_refunds'
726+ values.update({
727+ 'set_type': set_type,
728+ 'so_from_po': company.so_from_po,
729+ 'po_from_so': company.po_from_so,
730+ 'auto_validation' : company.auto_validation,
731+ 'warehouse_id' : company.warehouse_id.id,
732+ })
733+ return {'value': values}
734+
735+ def set_inter_company_configuration(self, cr, uid, ids, context=None):
736+ config = self.browse(cr, uid, ids[0], context=context)
737+ if config.company_id.id:
738+ vals = {
739+ 'so_from_po': config.so_from_po,
740+ 'po_from_so': config.po_from_so,
741+ 'auto_validation': config.auto_validation,
742+ 'auto_generate_invoices': True if config.set_type == 'invoice_and_refunds' else False,
743+ 'warehouse_id': config.warehouse_id.id
744+ }
745+ return self.pool.get('res.company').write(cr, uid, [config.company_id.id], vals, context=context)
746+
747+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
748
749=== added file 'inter_company_rules/res_config_view.xml'
750--- inter_company_rules/res_config_view.xml 1970-01-01 00:00:00 +0000
751+++ inter_company_rules/res_config_view.xml 2014-04-28 12:01:12 +0000
752@@ -0,0 +1,34 @@
753+<?xml version="1.0" encoding="utf-8"?>
754+<openerp>
755+ <data>
756+ <record id="view_general_configuration_inter_company_rules" model="ir.ui.view">
757+ <field name="name">base.config.settings.inter.company.rules</field>
758+ <field name="model">base.config.settings</field>
759+ <field name="inherit_id" ref="base_setup.view_general_configuration"/>
760+ <field name="arch" type="xml">
761+ <xpath expr="//div[@name='module_inter_company_rules']" position='after'>
762+ <div name="module_inter_company_rules_company_id" attrs="{'invisible': [('module_inter_company_rules', '=', False)]}">
763+ <field name="company_id" widget="selection" class="oe_inline" on_change="onchange_company_id(company_id, context)"/>
764+ <field name="set_type" widget="radio" on_change="onchange_set_type(set_type, context)"/>
765+ </div>
766+ <div name="module_inter_company_rules_set_so_po" attrs="{'invisible': ['|', ('set_type', '!=', 'so_and_po'), ('module_inter_company_rules', '=', False)]}">
767+ <field name="so_from_po"/>
768+ <label for="so_from_po"/>
769+ </div>
770+ <div name="module_inter_company_rules_set_po_so" attrs="{'invisible': ['|', ('set_type', '!=', 'so_and_po'), ('module_inter_company_rules', '=', False)]}">
771+ <field name="po_from_so"/>
772+ <label for="po_from_so"/>
773+ </div>
774+ <div name="module_inter_company_rules_warehouse_id" attrs="{'invisible': ['|', ('po_from_so', '=', False), ('module_inter_company_rules', '=', False)]}">
775+ <label for="warehouse_id"/>
776+ <field name="warehouse_id" class="oe_inline" attrs="{'required': [('po_from_so', '=', True), ('module_inter_company_rules', '=', True)]}" domain="[('company_id', '=', company_id)]"/>
777+ </div>
778+ <div name="module_inter_company_rules_set_auto_validation" attrs="{'invisible': ['|', ('set_type', '!=', 'so_and_po'), ('module_inter_company_rules', '=', False)]}">
779+ <field name="auto_validation"/>
780+ <label for="auto_validation"/>
781+ </div>
782+ </xpath>
783+ </field>
784+ </record>
785+ </data>
786+</openerp>
787
788=== added directory 'inter_company_rules/test'
789=== added file 'inter_company_rules/test/inter_company_invoice.yml'
790--- inter_company_rules/test/inter_company_invoice.yml 1970-01-01 00:00:00 +0000
791+++ inter_company_rules/test/inter_company_invoice.yml 2014-04-28 12:01:12 +0000
792@@ -0,0 +1,79 @@
793+-
794+ In order to check Inter-Company Rule of creating Supplier invoice from Customer invoice
795+ make 'Create Invoices/Refunds when encoding invoices/refunds made' true.
796+-
797+ !record {model: res.company, id: company_a}:
798+ so_from_po: 0
799+ po_from_so: 0
800+ auto_generate_invoices: 1
801+-
802+ !record {model: res.company, id: company_b}:
803+ so_from_po: 0
804+ po_from_so: 0
805+ auto_generate_invoices: 1
806+-
807+ I apply account user group to Company A user.
808+-
809+ !record {model: res.users, id: res_users_company_a}:
810+ groups_id:
811+ - account.group_account_user
812+-
813+ I will create customer invoice with account user.
814+-
815+ !context
816+ uid: 'res_users_company_a'
817+-
818+ !record {model: account.account, id: account_expense_company_a}:
819+ name: Expenses
820+ code: X1000
821+ type: other
822+ user_type: account.data_account_type_expense
823+ company_id: company_a
824+-
825+ Create an invoice for the partner of company B with amount 450.0
826+-
827+ !record {model: account.invoice, id: account_invoice_0}:
828+ company_id: company_a
829+ partner_id: partner_company_b
830+ currency_id: base.EUR
831+ invoice_line:
832+ - product_id: product_consultant
833+ price_unit: 450.0
834+ account_id: account_expense_company_a
835+ quantity: 1.0
836+-
837+ I check that the customer invoice is in draft state.
838+-
839+ !assert {model: account.invoice, id: account_invoice_0}:
840+ - state == 'draft'
841+-
842+ I validate the invoice.
843+-
844+ !workflow {model: account.invoice, action: invoice_open, ref: account_invoice_0}
845+-
846+ Now apply account user group to User B and test with that user.
847+-
848+ !record {model: res.users, id: res_users_company_b}:
849+ groups_id:
850+ - account.group_account_user
851+-
852+ !context
853+ uid: 'res_users_company_b'
854+-
855+ I check that the supplier invoice is created with proper data.
856+-
857+ !python {model: account.invoice}: |
858+ supplier_invoices = self.search(cr, uid, [('type','=','in_invoice')], context=context)
859+ supplier_invoice = self.browse(cr, uid, supplier_invoices[0], context=context)
860+
861+ #check invoice line is created with proper data
862+ assert supplier_invoice.invoice_line[0].quantity == 1, "Quantity in invoice line is incorrect."
863+ assert supplier_invoice.invoice_line[0].product_id.id == ref("product_consultant"), "Product in line is incorrect."
864+ assert supplier_invoice.invoice_line[0].price_unit == 450, "Unit Price in invoice line is incorrect."
865+ 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."
866+
867+ assert supplier_invoice.state == "draft", "invoice should be in draft state."
868+ assert supplier_invoice.amount_total == 450.0, "Total amount is incorrect."
869+ assert supplier_invoice.company_id.id == ref("company_b"), "Applied company in created invoice is incorrect."
870+ assert supplier_invoice.account_id.company_id.id == ref("company_b"), "Applied account in created invoice is not relevant to company."
871+
872
873=== added file 'inter_company_rules/test/inter_company_po_to_so.yml'
874--- inter_company_rules/test/inter_company_po_to_so.yml 1970-01-01 00:00:00 +0000
875+++ inter_company_rules/test/inter_company_po_to_so.yml 2014-04-28 12:01:12 +0000
876@@ -0,0 +1,42 @@
877+-
878+ In order to check Inter-Company Rule of creating SO from PO, I login with user company A
879+-
880+ !context
881+ uid: 'res_users_company_a'
882+-
883+ Quotation of purchase order.
884+-
885+ !record {model: purchase.order, id: purchase_order_cmpa}:
886+ partner_id: partner_company_b
887+ company_id: company_a
888+ order_line:
889+ - product_id: product_consultant
890+ name: 'Service'
891+ price_unit: 450.0
892+-
893+ I confirm the order.
894+-
895+ !workflow {model: purchase.order, action: purchase_confirm, ref: purchase_order_cmpa}
896+-
897+ Login with user of company B and check sale order.
898+-
899+ !context
900+ uid: 'res_users_company_b'
901+-
902+ I check that the Quotation of sale order is created with proper data.
903+-
904+ !python {model: sale.order}: |
905+ #NOTE: Browse PO with superuser id because of different company of user.
906+ po_name = self.pool.get('purchase.order').browse(cr, 1, ref("purchase_order_cmpa"), context=context).name
907+ sale_order_ids = self.search(cr, uid, [('client_order_ref', '=', po_name)], context=context)
908+ sale_order= self.browse(cr, uid, sale_order_ids[0], context=context)
909+ assert sale_order.state == "draft", "sale order should be in draft state."
910+ assert sale_order.partner_id.id == ref("partner_company_a"), "Supplier is not correspond to Company A."
911+ assert sale_order.company_id.id == ref("company_b"), "Applied company in created sale order is incorrect."
912+ assert sale_order.amount_total == 450.0, "Total amount is incorrect."
913+
914+ #check for order line
915+ assert sale_order.order_line[0].product_id.id == ref("product_consultant"), "Product in line is incorrect."
916+ assert sale_order.order_line[0].name == 'Service', "Product name is incorrect."
917+ assert sale_order.order_line[0].product_uom_qty == 1 , "Product qty is incorrect."
918+ assert sale_order.order_line[0].price_unit == 450, "Unit Price in line is incorrect."
919
920=== added file 'inter_company_rules/test/inter_company_so_to_po.yml'
921--- inter_company_rules/test/inter_company_so_to_po.yml 1970-01-01 00:00:00 +0000
922+++ inter_company_rules/test/inter_company_so_to_po.yml 2014-04-28 12:01:12 +0000
923@@ -0,0 +1,46 @@
924+-
925+ In order to check Inter-Company Rule of creating PO from SO, I login with user company A
926+-
927+ !context
928+ uid: 'res_users_company_a'
929+-
930+ I create Quotation of sale order and confirm it.
931+-
932+ !record {model: sale.order, id: sale_order_cmpa, view: False}:
933+ company_id: company_a
934+ warehouse_id: warehouse_company_a
935+ user_id: res_users_company_a
936+ pricelist_id: 1
937+ partner_id: partner_company_b
938+ partner_invoice_id: partner_company_b
939+ partner_shipping_id: partner_company_b
940+ order_line:
941+ - product_id: product_consultant
942+ name: 'Service'
943+ price_unit: 450.0
944+-
945+ I confirm the order.
946+-
947+ !python {model: sale.order}: |
948+ self.action_button_confirm(cr, uid, [ref('sale_order_cmpa')], context=context)
949+-
950+ I login with user company B.
951+-
952+ !context
953+ uid: 'res_users_company_b'
954+-
955+ I check that Quotation of purchase order and order line is same as sale order
956+-
957+ !python {model: purchase.order}: |
958+ order_ids = self.search(cr, uid, [('company_id','=',ref("company_b"))], context=context)
959+ purchase_order = self.browse(cr, uid, order_ids[0], context=context)
960+ assert purchase_order.state == "draft", "Invoice should be in draft state."
961+ assert purchase_order.partner_id.id == ref("partner_company_a"),"Supplier is not correspond to Company A."
962+ assert purchase_order.company_id.id == ref("company_b"),"Company is not correspond to purchase order."
963+ assert purchase_order.amount_total == 450.0, "Total amount is incorrect."
964+
965+ assert purchase_order.order_line[0].product_id.id == ref("product_consultant"), "Product in line is incorrect."
966+ assert purchase_order.order_line[0].name == 'Service',"Product name is incorrect."
967+ assert purchase_order.order_line[0].price_unit == 450 , "Price unit is incorrect."
968+ assert purchase_order.order_line[0].product_qty == 1 , "Product qty is incorrect."
969+ assert purchase_order.order_line[0].price_subtotal == 450, "line total is incorrect."
970
971=== added file 'inter_company_rules/test/test_intercompany_data.yml'
972--- inter_company_rules/test/test_intercompany_data.yml 1970-01-01 00:00:00 +0000
973+++ inter_company_rules/test/test_intercompany_data.yml 2014-04-28 12:01:12 +0000
974@@ -0,0 +1,145 @@
975+-
976+ !record {model: res.partner, id: partner_company_a, view: False}:
977+ name: Company A
978+ supplier: 1
979+-
980+ !record {model: res.company, id: company_a, view: False}:
981+ name: Company A
982+ parent_id: base.main_company
983+ currency_id: base.EUR
984+ partner_id: partner_company_a
985+ so_from_po: 1
986+ po_from_so: 1
987+-
988+ !record {model: stock.location, id: location_stock_company_a, view: False}:
989+ name: Stock - A
990+ usage: internal
991+ company_id: company_a
992+-
993+ !record {model: stock.location, id: location_output_company_a, view: False}:
994+ name: Output - A
995+ usage: internal
996+ company_id: company_a
997+-
998+ !record {model: stock.warehouse, id: warehouse_company_a, view: False}:
999+ name: purchase warehouse - A
1000+ lot_input_id: location_stock_company_a
1001+ lot_stock_id: location_stock_company_a
1002+ lot_output_id: location_output_company_a
1003+ company_id: company_a
1004+-
1005+ !record {model: res.partner, id: partner_company_b, view: False}:
1006+ name: Company B
1007+ supplier: 1
1008+-
1009+ !record {model: res.company, id: company_b, view: False}:
1010+ name: Company B
1011+ parent_id: base.main_company
1012+ currency_id: base.EUR
1013+ partner_id: partner_company_b
1014+ so_from_po: 1
1015+ po_from_so: 1
1016+-
1017+ !record {model: stock.location, id: location_stock_company_b, view: False}:
1018+ name: Stock - B
1019+ usage: internal
1020+ company_id: company_b
1021+-
1022+ !record {model: stock.location, id: location_output_company_b, view: False}:
1023+ name: Output - B
1024+ usage: internal
1025+ company_id: company_b
1026+-
1027+ !record {model: stock.warehouse, id: warehouse_company_b, view: False}:
1028+ name: purchase warehouse - B
1029+ lot_input_id: location_stock_company_b
1030+ lot_stock_id: location_stock_company_b
1031+ lot_output_id: location_output_company_b
1032+ company_id: company_b
1033+-
1034+ Apply warehouse in company.
1035+-
1036+ !record {model: res.company, id: company_a}:
1037+ warehouse_id: warehouse_company_a
1038+-
1039+ !record {model: res.company, id: company_b}:
1040+ warehouse_id: warehouse_company_b
1041+-
1042+ !record {model: product.product, id: product_consultant, view: False}:
1043+ name: Service
1044+ uom_id: product.product_uom_hour
1045+ uom_po_id: product.product_uom_hour
1046+ categ_id: product.product_category_all
1047+ type: service
1048+ company_id: False
1049+-
1050+ Create a user for each company
1051+-
1052+ !record {model: res.users, id: res_users_company_a, view: False}:
1053+ name: User A
1054+ login: usera
1055+ password: usera
1056+ email: usera@yourcompany.com
1057+ company_id: company_a
1058+ company_ids:
1059+ - company_a
1060+ groups_id:
1061+ - base.group_sale_salesman
1062+ - purchase.group_purchase_user
1063+-
1064+ !record {model: res.users, id: res_users_company_b, view: False}:
1065+ name: User B
1066+ login: userb
1067+ password: userb
1068+ email: userb@yourcompany.com
1069+ company_id: company_b
1070+ company_ids:
1071+ - company_b
1072+ groups_id:
1073+ - base.group_sale_salesman
1074+ - purchase.group_purchase_user
1075+-
1076+ Install COA for new companies.
1077+-
1078+ !python {model: account.installer}: |
1079+ wiz_comp_a = self.create(cr, uid, {'charts': 'configurable', 'company_id': ref("company_a")})
1080+ self.execute(cr, uid, [wiz_comp_a])
1081+ wiz_comp_b = self.create(cr, uid, {'charts': 'configurable', 'company_id': ref("company_b")})
1082+ self.execute(cr, uid, [wiz_comp_b])
1083+-
1084+ !python {model: wizard.multi.charts.accounts}: |
1085+ wiz_comp_a = self.create(cr, uid, {
1086+ 'chart_template_id': '1',
1087+ 'company_id': ref("company_a"),
1088+ 'currency_id': ref("base.EUR"),
1089+ 'code_digits': 6,
1090+ 'purchase_tax': False,
1091+ 'sale_tax': False})
1092+ self.execute(cr, uid, [wiz_comp_a])
1093+ wiz_comp_b = self.create(cr, uid, {
1094+ 'chart_template_id': '1',
1095+ 'company_id': ref("company_b"),
1096+ 'currency_id': ref("base.EUR"),
1097+ 'code_digits': 6,
1098+ 'purchase_tax': False,
1099+ 'sale_tax': False})
1100+ self.execute(cr, uid, [wiz_comp_b])
1101+-
1102+ Now remove company from related partners of company and apply default accounts.
1103+-
1104+ !python {model: res.partner}: |
1105+ account_obj = self.pool.get("account.account")
1106+ account_receivable_a = account_obj.search(cr, uid, [('company_id', '=', ref("company_a")), ('type', '=', 'receivable')], context=context)
1107+ account_payable_a = account_obj.search(cr, uid, [('company_id', '=', ref("company_a")), ('type', '=', 'payable')], context=context)
1108+ self.write(cr, uid, [ref("partner_company_a")], {
1109+ 'company_id': False,
1110+ 'property_account_receivable': account_receivable_a[0],
1111+ 'property_account_payable': account_payable_a[0]
1112+ }, context=context)
1113+ account_receivable_b = account_obj.search(cr, uid, [('company_id', '=', ref("company_b")), ('type', '=', 'receivable')], context=context)
1114+ account_payable_b = account_obj.search(cr, uid, [('company_id', '=', ref("company_b")), ('type', '=', 'payable')], context=context)
1115+ self.write(cr, uid, [ref("partner_company_b")], {
1116+ 'company_id': False,
1117+ 'property_account_receivable': account_receivable_b[0],
1118+ 'property_account_payable': account_payable_b[0]
1119+ }, context=context)

Subscribers

People subscribed via source and target branches

to all changes: