[IMP] Not necessary from merge
[odoo/odoo.git] / addons / purchase / stock.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21
22 from openerp.osv import fields, osv
23 from openerp.tools.translate import _
24
25 class stock_move(osv.osv):
26     _inherit = 'stock.move'
27     _columns = {
28         'purchase_line_id': fields.many2one('purchase.order.line',
29             'Purchase Order Line', ondelete='set null', select=True,
30             readonly=True),
31     }
32
33     def write(self, cr, uid, ids, vals, context=None):
34         if isinstance(ids, (int, long)):
35             ids = [ids]
36         res = super(stock_move, self).write(cr, uid, ids, vals, context=context)
37         from openerp import workflow
38         if vals.get('state') in ['done', 'cancel']:
39             for move in self.browse(cr, uid, ids, context=context):
40                 if move.purchase_line_id and move.purchase_line_id.order_id:
41                     order_id = move.purchase_line_id.order_id.id
42                     if self.pool.get('purchase.order').test_moves_done(cr, uid, [order_id], context=context):
43                         workflow.trg_validate(uid, 'purchase.order', order_id, 'picking_done', cr)
44                     if self.pool.get('purchase.order').test_moves_except(cr, uid, [order_id], context=context):
45                         workflow.trg_validate(uid, 'purchase.order', order_id, 'picking_cancel', cr)
46         return res
47
48     def copy(self, cr, uid, id, default=None, context=None):
49         default = default or {}
50         context = context or {}
51         if not default.get('split_from'):
52             #we don't want to propagate the link to the purchase order line except in case of move split
53             default['purchase_line_id'] = False
54         return super(stock_move, self).copy(cr, uid, id, default, context)
55
56
57     def _create_invoice_line_from_vals(self, cr, uid, move, invoice_line_vals, context=None):
58         invoice_line_id = super(stock_move, self)._create_invoice_line_from_vals(cr, uid, move, invoice_line_vals, context=context)
59         if move.purchase_line_id:
60             purchase_line = move.purchase_line_id
61             self.pool.get('purchase.order.line').write(cr, uid, [purchase_line.id], {
62                 'invoice_lines': [(4, invoice_line_id)]
63             }, context=context)
64             self.pool.get('purchase.order').write(cr, uid, [purchase_line.order_id.id], {
65                 'invoice_ids': [(4, invoice_line_vals['invoice_id'])],
66             })
67         return invoice_line_id
68
69     def _get_master_data(self, cr, uid, move, company, context=None):
70         if move.purchase_line_id:
71             purchase_order = move.purchase_line_id.order_id
72             return purchase_order.partner_id, purchase_order.create_uid.id, purchase_order.pricelist_id.currency_id.id
73         return super(stock_move, self)._get_master_data(cr, uid, move, company, context=context)
74
75     def _get_invoice_line_vals(self, cr, uid, move, partner, inv_type, context=None):
76         res = super(stock_move, self)._get_invoice_line_vals(cr, uid, move, partner, inv_type, context=context)
77         if move.purchase_line_id:
78             purchase_line = move.purchase_line_id
79             res['invoice_line_tax_id'] = [(6, 0, [x.id for x in purchase_line.taxes_id])]
80             res['price_unit'] = purchase_line.price_unit
81         return res
82
83 class stock_picking(osv.osv):
84     _inherit = 'stock.picking'
85     
86     def _get_to_invoice(self, cr, uid, ids, name, args, context=None):
87         res = {}
88         for picking in self.browse(cr, uid, ids, context=context):
89             res[picking.id] = False
90             for move in picking.move_lines:
91                 if move.purchase_line_id and move.purchase_line_id.order_id.invoice_method == 'picking':
92                     if not move.move_orig_ids:
93                         res[picking.id] = True
94         return res
95
96     def _get_picking_to_recompute(self, cr, uid, ids, context=None):
97         picking_ids = set()
98         for move in self.pool.get('stock.move').browse(cr, uid, ids, context=context):
99             if move.picking_id and move.purchase_line_id:
100                 picking_ids.add(move.picking_id.id)
101         return list(picking_ids)
102
103     _columns = {
104         'reception_to_invoice': fields.function(_get_to_invoice, type='boolean', string='Invoiceable on incoming shipment?',
105                help='Does the picking contains some moves related to a purchase order invoiceable on the receipt?',
106                store={
107                    'stock.move': (_get_picking_to_recompute, ['purchase_line_id', 'picking_id'], 10),
108                }),
109     }
110
111
112 class stock_warehouse(osv.osv):
113     _inherit = 'stock.warehouse'
114     _columns = {
115         'buy_to_resupply': fields.boolean('Purchase to resupply this warehouse', 
116                                           help="When products are bought, they can be delivered to this warehouse"),
117         'buy_pull_id': fields.many2one('procurement.rule', 'BUY rule'),
118     }
119     _defaults = {
120         'buy_to_resupply': True,
121     }
122
123     def _get_buy_pull_rule(self, cr, uid, warehouse, context=None):
124         route_obj = self.pool.get('stock.location.route')
125         data_obj = self.pool.get('ir.model.data')
126         try:
127             buy_route_id = data_obj.get_object_reference(cr, uid, 'stock', 'route_warehouse0_buy')[1]
128         except:
129             buy_route_id = route_obj.search(cr, uid, [('name', 'like', _('Buy'))], context=context)
130             buy_route_id = buy_route_id and buy_route_id[0] or False
131         if not buy_route_id:
132             raise osv.except_osv(_('Error!'), _('Can\'t find any generic Buy route.'))
133
134         return {
135             'name': self._format_routename(cr, uid, warehouse, _(' Buy'), context=context),
136             'location_id': warehouse.in_type_id.default_location_dest_id.id,
137             'route_id': buy_route_id,
138             'action': 'buy',
139             'picking_type_id': warehouse.in_type_id.id,
140             'propagate': False, 
141             'warehouse_id': warehouse.id,
142         }
143
144     def create_routes(self, cr, uid, ids, warehouse, context=None):
145         pull_obj = self.pool.get('procurement.rule')
146         res = super(stock_warehouse, self).create_routes(cr, uid, ids, warehouse, context=context)
147         if warehouse.buy_to_resupply:
148             buy_pull_vals = self._get_buy_pull_rule(cr, uid, warehouse, context=context)
149             buy_pull_id = pull_obj.create(cr, uid, buy_pull_vals, context=context)
150             res['buy_pull_id'] = buy_pull_id
151         return res
152
153     def write(self, cr, uid, ids, vals, context=None):
154         pull_obj = self.pool.get('procurement.rule')
155         if isinstance(ids, (int, long)):
156             ids = [ids]
157
158         if 'buy_to_resupply' in vals:
159             if vals.get("buy_to_resupply"):
160                 for warehouse in self.browse(cr, uid, ids, context=context):
161                     if not warehouse.buy_pull_id:
162                         buy_pull_vals = self._get_buy_pull_rule(cr, uid, warehouse, context=context)
163                         buy_pull_id = pull_obj.create(cr, uid, buy_pull_vals, context=context)
164                         vals['buy_pull_id'] = buy_pull_id
165             else:
166                 for warehouse in self.browse(cr, uid, ids, context=context):
167                     if warehouse.buy_pull_id:
168                         buy_pull_id = pull_obj.unlink(cr, uid, warehouse.buy_pull_id.id, context=context)
169         return super(stock_warehouse, self).write(cr, uid, ids, vals, context=None)
170
171     def get_all_routes_for_wh(self, cr, uid, warehouse, context=None):
172         all_routes = super(stock_warehouse, self).get_all_routes_for_wh(cr, uid, warehouse, context=context)
173         if warehouse.buy_to_resupply and warehouse.buy_pull_id and warehouse.buy_pull_id.route_id:
174             all_routes += [warehouse.buy_pull_id.route_id.id]
175         return all_routes
176
177     def _get_all_products_to_resupply(self, cr, uid, warehouse, context=None):
178         res = super(stock_warehouse, self)._get_all_products_to_resupply(cr, uid, warehouse, context=context)
179         if warehouse.buy_pull_id and warehouse.buy_pull_id.route_id:
180             for product_id in res:
181                 for route in self.pool.get('product.product').browse(cr, uid, product_id, context=context).route_ids:
182                     if route.id == warehouse.buy_pull_id.route_id.id:
183                         res.remove(product_id)
184                         break
185         return res
186
187     def _handle_renaming(self, cr, uid, warehouse, name, code, context=None):
188         res = super(stock_warehouse, self)._handle_renaming(cr, uid, warehouse, name, code, context=context)
189         pull_obj = self.pool.get('procurement.rule')
190         #change the buy pull rule name
191         if warehouse.buy_pull_id:
192             pull_obj.write(cr, uid, warehouse.buy_pull_id.id, {'name': warehouse.buy_pull_id.name.replace(warehouse.name, name, 1)}, context=context)
193         return res
194
195     def change_route(self, cr, uid, ids, warehouse, new_reception_step=False, new_delivery_step=False, context=None):
196         res = super(stock_warehouse, self).change_route(cr, uid, ids, warehouse, new_reception_step=new_reception_step, new_delivery_step=new_delivery_step, context=context)
197         if warehouse.in_type_id.default_location_dest_id != warehouse.buy_pull_id.location_id:
198             self.pool.get('procurement.rule').write(cr, uid, warehouse.buy_pull_id.id, {'location_id': warehouse.in_type_id.default_location_dest_id.id}, context=context)
199         return res