[FIX] action_confirm was not asking for purchase pricelist, otherwise it was asking...
[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     def _create_invoice_line_from_vals(self, cr, uid, move, invoice_line_vals, context=None):
57         if move.purchase_line_id:
58             invoice_line_vals['purchase_line_id'] = move.purchase_line_id.id
59         invoice_line_id = super(stock_move, self)._create_invoice_line_from_vals(cr, uid, move, invoice_line_vals, context=context)
60         if move.purchase_line_id:
61             purchase_line = move.purchase_line_id
62             self.pool.get('purchase.order.line').write(cr, uid, [purchase_line.id], {
63                 'invoice_lines': [(4, invoice_line_id)]
64             }, context=context)
65             self.pool.get('purchase.order').write(cr, uid, [purchase_line.order_id.id], {
66                 'invoice_ids': [(4, invoice_line_vals['invoice_id'])],
67             })
68         return invoice_line_id
69
70     def _get_master_data(self, cr, uid, move, company, context=None):
71         if move.purchase_line_id:
72             purchase_order = move.purchase_line_id.order_id
73             return purchase_order.partner_id, purchase_order.create_uid.id, purchase_order.currency_id.id
74         else:
75             partner = move.picking_id and move.picking_id.partner_id or False
76             code = self.get_code_from_locs(cr, uid, move, context=context)
77             if partner and partner.property_product_pricelist_purchase and code == 'incoming':
78                 currency = partner.property_product_pricelist_purchase.currency_id.id
79                 return partner, uid, currency
80         return super(stock_move, self)._get_master_data(cr, uid, move, company, context=context)
81
82
83     def _get_invoice_line_vals(self, cr, uid, move, partner, inv_type, context=None):
84         res = super(stock_move, self)._get_invoice_line_vals(cr, uid, move, partner, inv_type, context=context)
85         if move.purchase_line_id:
86             purchase_line = move.purchase_line_id
87             res['invoice_line_tax_id'] = [(6, 0, [x.id for x in purchase_line.taxes_id])]
88             res['price_unit'] = purchase_line.price_unit
89         return res
90
91
92     def attribute_price(self, cr, uid, move, context=None):
93         """
94             Attribute price to move, important in inter-company moves or receipts with only one partner
95         """
96         code = self.get_code_from_locs(cr, uid, move, context=context)
97         if not move.purchase_line_id and code == 'incoming' and not move.price_unit:
98             partner = move.picking_id and move.picking_id.partner_id or False
99             price = False
100             # If partner given, search price in its purchase pricelist
101             if partner and partner.property_product_pricelist_purchase:
102                 pricelist_obj = self.pool.get("product.pricelist")
103                 pricelist = partner.property_product_pricelist_purchase.id
104                 price = pricelist_obj.price_get(cr, uid, [pricelist],
105                                     move.product_id.id, move.product_uom_qty, partner, {
106                                                                                 'uom': move.product_uom.id,
107                                                                                 'date': move.date,
108                                                                                 })[pricelist]
109                 if price:
110                     return self.write(cr, uid, [move.id], {'price_unit': price}, context=context)
111         super(stock_move, self).attribute_price(cr, uid, move, context=context)
112
113
114 class stock_picking(osv.osv):
115     _inherit = 'stock.picking'
116     
117     def _get_to_invoice(self, cr, uid, ids, name, args, context=None):
118         res = {}
119         for picking in self.browse(cr, uid, ids, context=context):
120             res[picking.id] = False
121             for move in picking.move_lines:
122                 if move.purchase_line_id and move.purchase_line_id.order_id.invoice_method == 'picking':
123                     if not move.move_orig_ids:
124                         res[picking.id] = True
125         return res
126
127     def _get_picking_to_recompute(self, cr, uid, ids, context=None):
128         picking_ids = set()
129         for move in self.pool.get('stock.move').browse(cr, uid, ids, context=context):
130             if move.picking_id and move.purchase_line_id:
131                 picking_ids.add(move.picking_id.id)
132         return list(picking_ids)
133
134     _columns = {
135         'reception_to_invoice': fields.function(_get_to_invoice, type='boolean', string='Invoiceable on incoming shipment?',
136                help='Does the picking contains some moves related to a purchase order invoiceable on the receipt?',
137                store={
138                    'stock.move': (_get_picking_to_recompute, ['purchase_line_id', 'picking_id'], 10),
139                }),
140     }
141
142     def _create_invoice_from_picking(self, cr, uid, picking, vals, context=None):
143         purchase_obj = self.pool.get("purchase.order")
144         purchase_line_obj = self.pool.get('purchase.order.line')
145         invoice_line_obj = self.pool.get('account.invoice.line')
146         invoice_id = super(stock_picking, self)._create_invoice_from_picking(cr, uid, picking, vals, context=context)
147         if picking.move_lines and picking.move_lines[0].purchase_line_id:
148             purchase_id = picking.move_lines[0].purchase_line_id.order_id.id
149             purchase_line_ids = purchase_line_obj.search(cr, uid, [('order_id', '=', purchase_id), ('product_id.type', '=', 'service'), ('invoiced', '=', False)], context=context)
150             if purchase_line_ids:
151                 inv_lines = []
152                 for po_line in purchase_line_obj.browse(cr, uid, purchase_line_ids, context=context):
153                     acc_id = purchase_obj._choose_account_from_po_line(cr, uid, po_line, context=context)
154                     inv_line_data = purchase_obj._prepare_inv_line(cr, uid, acc_id, po_line, context=context)
155                     inv_line_id = invoice_line_obj.create(cr, uid, inv_line_data, context=context)
156                     inv_lines.append(inv_line_id)
157                     po_line.write({'invoice_lines': [(4, inv_line_id)]})
158                 invoice_line_obj.write(cr, uid, inv_lines, {'invoice_id': invoice_id}, context=context)
159         return invoice_id
160
161
162 class stock_warehouse(osv.osv):
163     _inherit = 'stock.warehouse'
164     _columns = {
165         'buy_to_resupply': fields.boolean('Purchase to resupply this warehouse', 
166                                           help="When products are bought, they can be delivered to this warehouse"),
167         'buy_pull_id': fields.many2one('procurement.rule', 'BUY rule'),
168     }
169     _defaults = {
170         'buy_to_resupply': True,
171     }
172
173     def _get_buy_pull_rule(self, cr, uid, warehouse, context=None):
174         route_obj = self.pool.get('stock.location.route')
175         data_obj = self.pool.get('ir.model.data')
176         try:
177             buy_route_id = data_obj.get_object_reference(cr, uid, 'purchase', 'route_warehouse0_buy')[1]
178         except:
179             buy_route_id = route_obj.search(cr, uid, [('name', 'like', _('Buy'))], context=context)
180             buy_route_id = buy_route_id and buy_route_id[0] or False
181         if not buy_route_id:
182             raise osv.except_osv(_('Error!'), _('Can\'t find any generic Buy route.'))
183
184         return {
185             'name': self._format_routename(cr, uid, warehouse, _(' Buy'), context=context),
186             'location_id': warehouse.in_type_id.default_location_dest_id.id,
187             'route_id': buy_route_id,
188             'action': 'buy',
189             'picking_type_id': warehouse.in_type_id.id,
190             'warehouse_id': warehouse.id,
191         }
192
193     def create_routes(self, cr, uid, ids, warehouse, context=None):
194         pull_obj = self.pool.get('procurement.rule')
195         res = super(stock_warehouse, self).create_routes(cr, uid, ids, warehouse, context=context)
196         if warehouse.buy_to_resupply:
197             buy_pull_vals = self._get_buy_pull_rule(cr, uid, warehouse, context=context)
198             buy_pull_id = pull_obj.create(cr, uid, buy_pull_vals, context=context)
199             res['buy_pull_id'] = buy_pull_id
200         return res
201
202     def write(self, cr, uid, ids, vals, context=None):
203         pull_obj = self.pool.get('procurement.rule')
204         if isinstance(ids, (int, long)):
205             ids = [ids]
206
207         if 'buy_to_resupply' in vals:
208             if vals.get("buy_to_resupply"):
209                 for warehouse in self.browse(cr, uid, ids, context=context):
210                     if not warehouse.buy_pull_id:
211                         buy_pull_vals = self._get_buy_pull_rule(cr, uid, warehouse, context=context)
212                         buy_pull_id = pull_obj.create(cr, uid, buy_pull_vals, context=context)
213                         vals['buy_pull_id'] = buy_pull_id
214             else:
215                 for warehouse in self.browse(cr, uid, ids, context=context):
216                     if warehouse.buy_pull_id:
217                         buy_pull_id = pull_obj.unlink(cr, uid, warehouse.buy_pull_id.id, context=context)
218         return super(stock_warehouse, self).write(cr, uid, ids, vals, context=None)
219
220     def get_all_routes_for_wh(self, cr, uid, warehouse, context=None):
221         all_routes = super(stock_warehouse, self).get_all_routes_for_wh(cr, uid, warehouse, context=context)
222         if warehouse.buy_to_resupply and warehouse.buy_pull_id and warehouse.buy_pull_id.route_id:
223             all_routes += [warehouse.buy_pull_id.route_id.id]
224         return all_routes
225
226     def _get_all_products_to_resupply(self, cr, uid, warehouse, context=None):
227         res = super(stock_warehouse, self)._get_all_products_to_resupply(cr, uid, warehouse, context=context)
228         if warehouse.buy_pull_id and warehouse.buy_pull_id.route_id:
229             for product_id in res:
230                 for route in self.pool.get('product.product').browse(cr, uid, product_id, context=context).route_ids:
231                     if route.id == warehouse.buy_pull_id.route_id.id:
232                         res.remove(product_id)
233                         break
234         return res
235
236     def _handle_renaming(self, cr, uid, warehouse, name, code, context=None):
237         res = super(stock_warehouse, self)._handle_renaming(cr, uid, warehouse, name, code, context=context)
238         pull_obj = self.pool.get('procurement.rule')
239         #change the buy pull rule name
240         if warehouse.buy_pull_id:
241             pull_obj.write(cr, uid, warehouse.buy_pull_id.id, {'name': warehouse.buy_pull_id.name.replace(warehouse.name, name, 1)}, context=context)
242         return res
243
244     def change_route(self, cr, uid, ids, warehouse, new_reception_step=False, new_delivery_step=False, context=None):
245         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)
246         if warehouse.in_type_id.default_location_dest_id != warehouse.buy_pull_id.location_id:
247             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)
248         return res