Merge lp:~openerp-dev/openobject-addons/trunk-intercompany-rules-tpa into lp:openobject-addons
- trunk-intercompany-rules-tpa
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
OpenERP Core Team | Pending | ||
Review via email:
|
Commit message
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) |