1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
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.
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.
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/>.
20 ##############################################################################
22 from openerp.osv import fields, osv
23 from openerp.tools.translate import _
25 class stock_move(osv.osv):
26 _inherit = 'stock.move'
28 'purchase_line_id': fields.many2one('purchase.order.line',
29 'Purchase Order Line', ondelete='set null', select=True,
33 def write(self, cr, uid, ids, vals, context=None):
34 if isinstance(ids, (int, long)):
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)
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)
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)]
65 self.pool.get('purchase.order').write(cr, uid, [purchase_line.order_id.id], {
66 'invoice_ids': [(4, invoice_line_vals['invoice_id'])],
68 return invoice_line_id
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
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)
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
92 def attribute_price(self, cr, uid, move, context=None):
94 Attribute price to move, important in inter-company moves or receipts with only one partner
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
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.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,
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)
114 class stock_picking(osv.osv):
115 _inherit = 'stock.picking'
117 def _get_to_invoice(self, cr, uid, ids, name, args, context=None):
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
127 def _get_picking_to_recompute(self, cr, uid, ids, context=None):
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)
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?',
138 'stock.move': (_get_picking_to_recompute, ['purchase_line_id', 'picking_id'], 10),
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:
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)
162 class stock_warehouse(osv.osv):
163 _inherit = 'stock.warehouse'
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'),
170 'buy_to_resupply': True,
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')
177 buy_route_id = data_obj.get_object_reference(cr, uid, 'purchase', 'route_warehouse0_buy')[1]
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
182 raise osv.except_osv(_('Error!'), _('Can\'t find any generic Buy route.'))
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,
189 'picking_type_id': warehouse.in_type_id.id,
190 'warehouse_id': warehouse.id,
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
202 def write(self, cr, uid, ids, vals, context=None):
203 pull_obj = self.pool.get('procurement.rule')
204 if isinstance(ids, (int, long)):
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
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)
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]
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)
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)
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)