1
by jf
Initial Import |
1 |
#!/usr/bin/env python
|
2 |
# -*- encoding: utf-8 -*-
|
|
3 |
##############################################################################
|
|
4 |
#
|
|
5 |
# OpenERP, Open Source Management Solution
|
|
6 |
# Copyright (C) 2011 TeMPO Consulting, MSF
|
|
7 |
#
|
|
8 |
# This program is free software: you can redistribute it and/or modify
|
|
9 |
# it under the terms of the GNU Affero General Public License as
|
|
10 |
# published by the Free Software Foundation, either version 3 of the
|
|
11 |
# License, or (at your option) any later version.
|
|
12 |
#
|
|
13 |
# This program is distributed in the hope that it will be useful,
|
|
14 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
15 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
16 |
# GNU Affero General Public License for more details.
|
|
17 |
#
|
|
18 |
# You should have received a copy of the GNU Affero General Public License
|
|
19 |
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
20 |
#
|
|
21 |
##############################################################################
|
|
22 |
||
23 |
import time |
|
24 |
import netsvc |
|
25 |
||
26 |
from osv import osv |
|
27 |
from osv import fields |
|
28 |
from tools.translate import _ |
|
29 |
||
30 |
class procurement_list(osv.osv): |
|
31 |
_name = 'procurement.list' |
|
32 |
_description = 'Procurement list' |
|
33 |
||
34 |
_columns = { |
|
35 |
'name': fields.char(size=64, string='Ref.', required=True, readonly=True, |
|
36 |
states={'draft': [('readonly', False)]}), |
|
37 |
'requestor': fields.char(size=20, string='Requestor',), |
|
38 |
'order_date': fields.date(string='Order date', required=True), |
|
39 |
'warehouse_id': fields.many2one('stock.warehouse', string='Warehouse'), |
|
40 |
'origin': fields.char(size=64, string='Origin'), |
|
41 |
'state': fields.selection([('draft', 'Draft'),('done', 'Done'), ('cancel', 'Cancel')], |
|
42 |
string='State', readonly=True), |
|
43 |
'line_ids': fields.one2many('procurement.list.line', 'list_id', string='Lines', readonly=True, |
|
44 |
states={'draft': [('readonly', False)]}), |
|
45 |
'notes': fields.text(string='Notes'), |
|
46 |
'order_ids': fields.many2many('purchase.order', 'procurement_list_order_rel', |
|
47 |
'list_id', 'order_id', string='Orders', readonly=True), |
|
48 |
}
|
|
49 |
||
50 |
_defaults = { |
|
51 |
'name': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'procurement.list'), |
|
52 |
'state': lambda *a: 'draft', |
|
53 |
'order_date': lambda *a: time.strftime('%Y-%m-%d'), |
|
54 |
}
|
|
55 |
||
56 |
def copy(self, cr, uid, ids, default={}, context={}): |
|
57 |
'''
|
|
58 |
Increments the sequence for the new list
|
|
59 |
'''
|
|
60 |
default['name'] = self.pool.get('ir.sequence').get(cr, uid, 'procurement.list') |
|
61 |
default['order_ids'] = [] |
|
62 |
||
63 |
res = super(procurement_list, self).copy(cr, uid, ids, default, context=context) |
|
64 |
||
65 |
return res |
|
66 |
||
67 |
def cancel(self, cr, uid, ids, context={}): |
|
68 |
'''
|
|
69 |
Sets the procurement list to the 'Cancel' state
|
|
70 |
'''
|
|
71 |
self.write(cr, uid, ids, {'state': 'cancel'}) |
|
72 |
||
73 |
return True |
|
33
by jf
UF-108: [MERGE] default supp. sugg. |
74 |
|
75 |
def create_po(self, cr, uid, ids, context={}): |
|
76 |
'''
|
|
77 |
Creates all purchase orders according to choices on lines
|
|
78 |
'''
|
|
79 |
if ids and isinstance(ids, (int, long)): |
|
80 |
ids = [ids] |
|
81 |
||
82 |
order_obj = self.pool.get('purchase.order') |
|
83 |
order_line_obj = self.pool.get('purchase.order.line') |
|
84 |
proc_obj = self.pool.get('procurement.list') |
|
85 |
line_obj = self.pool.get('procurement.list.line') |
|
86 |
prod_sup_obj = self.pool.get('product.supplierinfo') |
|
87 |
||
88 |
# We search if at least one line hasn't defined supplier
|
|
89 |
for list in self.browse(cr, uid, ids): |
|
90 |
for line in list.line_ids: |
|
91 |
if not line.supplier_id: |
|
92 |
raise osv.except_osv(_('Error'), _('You cannot create purchase orders while all lines haven\'t a defined supplier')) |
|
93 |
||
94 |
# We search lines group by supplier_id
|
|
95 |
po_exists = {} |
|
96 |
po_id = False |
|
97 |
po_ids = [] |
|
98 |
line_ids = line_obj.search(cr, uid, [('list_id', 'in', ids)], order='supplier_id') |
|
99 |
for l in line_obj.browse(cr, uid, line_ids): |
|
100 |
# Search if a PO already exists in the system
|
|
101 |
if not po_exists.get(l.supplier_id.id, False): |
|
102 |
# If no PO in local memory, search in DB
|
|
103 |
po_exist = order_obj.search(cr, uid, [('origin', '=', l.list_id.name), ('partner_id', '=', l.supplier_id.id)]) |
|
104 |
if po_exist: |
|
105 |
# A PO exists in DB, set the id in local memory
|
|
106 |
po_exists[l.supplier_id.id] = po_exist[0] |
|
107 |
else: |
|
108 |
# try to create a new PO, and set its id in local memory
|
|
109 |
address = l.supplier_id.address_get().get('default') |
|
110 |
# Returns an error when the supplier has not defined address
|
|
111 |
if not address: |
|
112 |
raise osv.except_osv(_('Error'), _('The supplier %s has no address defined on its form' %l.supplier_id.name)) |
|
113 |
# When starting or when the supplier changed, we create a Purchase Order
|
|
114 |
po_id = order_obj.create(cr, uid, {'partner_id': l.supplier_id.id, |
|
115 |
'partner_address_id': address, |
|
116 |
'pricelist_id': l.supplier_id.property_product_pricelist.id, |
|
117 |
'origin': l.list_id.name, |
|
118 |
'location_id': proc_obj._get_location(cr, uid, l.list_id.warehouse_id)}) |
|
119 |
po_exists[l.supplier_id.id] = po_id |
|
120 |
||
121 |
# Get the PO id in local memory
|
|
122 |
po_id = po_exists.get(l.supplier_id.id) |
|
123 |
||
124 |
# We create all lines for this supplier
|
|
125 |
price_unit = prod_sup_obj.price_get(cr, uid, [l.supplier_id.id], l.product_id.id, l.product_qty) |
|
126 |
order_line_obj.create(cr, uid, {'product_uom': l.product_uom_id.id, |
|
127 |
'product_id': l.product_id.id, |
|
128 |
'order_id': po_id, |
|
129 |
'price_unit': price_unit[l.supplier_id.id], |
|
130 |
'date_planned': l.list_id.order_date, |
|
131 |
'product_qty': l.product_qty, |
|
132 |
'name': l.product_id.name,}) |
|
133 |
||
134 |
for supplier in po_exists: |
|
135 |
po_ids.append(po_exists.get(supplier)) |
|
136 |
||
137 |
# We confirm all created orders
|
|
138 |
wf_service = netsvc.LocalService("workflow") |
|
139 |
for po in po_ids: |
|
140 |
wf_service.trg_validate(uid, 'purchase.order', po, 'purchase_confirm', cr) |
|
141 |
||
142 |
proc_obj.write(cr, uid, ids[0], {'state': 'done', 'order_ids': [(6, 0, po_ids)]}) |
|
143 |
||
144 |
return {'type': 'ir.actions.act_window', |
|
145 |
'res_model': 'purchase.order', |
|
146 |
'view_type': 'form', |
|
147 |
'view_mode': 'tree,form', |
|
148 |
'domain': [('id', 'in', po_ids)], |
|
149 |
}
|
|
1
by jf
Initial Import |
150 |
|
151 |
def create_rfq(self, cr, uid, ids, context={}): |
|
152 |
'''
|
|
153 |
Create a RfQ per supplier with all products
|
|
154 |
'''
|
|
155 |
purchase_obj = self.pool.get('purchase.order') |
|
156 |
line_obj = self.pool.get('purchase.order.line') |
|
157 |
||
158 |
order_ids = [] |
|
159 |
||
160 |
for list in self.browse(cr, uid, ids, context=context): |
|
33
by jf
UF-108: [MERGE] default supp. sugg. |
161 |
# Returns an error message if no products defined
|
1
by jf
Initial Import |
162 |
if not list.line_ids or len(list.line_ids) == 0: |
163 |
raise osv.except_osv(_('Error'), _('No line defined for this list !')) |
|
164 |
||
165 |
location_id = self._get_location(cr, uid, list.warehouse_id) |
|
33
by jf
UF-108: [MERGE] default supp. sugg. |
166 |
|
167 |
context['active_ids'] = ids |
|
168 |
context['active_id'] = ids[0] |
|
1
by jf
Initial Import |
169 |
|
170 |
return {'type': 'ir.actions.act_window', |
|
33
by jf
UF-108: [MERGE] default supp. sugg. |
171 |
'res_model': 'procurement.choose.supplier.rfq', |
172 |
'target': 'new', |
|
1
by jf
Initial Import |
173 |
'view_type': 'form', |
33
by jf
UF-108: [MERGE] default supp. sugg. |
174 |
'view_mode': 'form', |
175 |
'context': context} |
|
1
by jf
Initial Import |
176 |
|
177 |
def reset(self, cr, uid, ids, context={}): |
|
178 |
'''
|
|
179 |
Sets the procurement list to the 'Draft' state
|
|
180 |
'''
|
|
181 |
self.write(cr, uid, ids, {'state': 'draft'}) |
|
182 |
||
183 |
return True |
|
184 |
||
185 |
def _get_location(self, cr, uid, warehouse=None): |
|
186 |
'''
|
|
187 |
Returns the default input location for product
|
|
188 |
'''
|
|
189 |
if warehouse: |
|
190 |
return warehouse.lot_input_id.id |
|
191 |
warehouse_obj = self.pool.get('stock.warehouse') |
|
192 |
warehouse_id = warehouse_obj.search(cr, uid, [])[0] |
|
193 |
return warehouse_obj.browse(cr, uid, warehouse_id).lot_input_id.id |
|
194 |
||
195 |
procurement_list() |
|
196 |
||
197 |
||
198 |
class procurement_list_line(osv.osv): |
|
199 |
_name = 'procurement.list.line' |
|
200 |
_description = 'Procurement line' |
|
201 |
_rec_name = 'product_id' |
|
202 |
||
203 |
_columns = { |
|
204 |
'product_id': fields.many2one('product.product', string='Product', required=True), |
|
205 |
'product_uom_id': fields.many2one('product.uom', string='UoM', required=True), |
|
206 |
'product_qty': fields.float(digits=(16,4), string='Quantity', required=True), |
|
207 |
'comment': fields.char(size=128, string='Comment'), |
|
208 |
'from_stock': fields.boolean(string='From stock ?'), |
|
209 |
'latest': fields.char(size=64, string='Latest document', readonly=True), |
|
210 |
'list_id': fields.many2one('procurement.list', string='List', required=True, ondelete='cascade'), |
|
33
by jf
UF-108: [MERGE] default supp. sugg. |
211 |
'supplier_id': fields.many2one('res.partner', string='Supplier'), |
1
by jf
Initial Import |
212 |
}
|
213 |
||
214 |
_defaults = { |
|
215 |
'latest': lambda *a: '', |
|
216 |
}
|
|
217 |
||
218 |
def copy_data(self, cr, uid, id, default={}, context={}): |
|
219 |
'''
|
|
220 |
Initializes the 'latest' fields to an empty field
|
|
221 |
'''
|
|
222 |
default['latest'] = '' |
|
223 |
||
224 |
res = super(procurement_list_line, self).copy_data(cr, uid, id, default, context=context) |
|
225 |
||
226 |
return res |
|
227 |
||
228 |
def product_id_change(self, cr, uid, ids, product_id, context={}): |
|
229 |
'''
|
|
230 |
Fills automatically the product_uom_id field on the line when the
|
|
231 |
product was changed.
|
|
232 |
'''
|
|
233 |
product_obj = self.pool.get('product.product') |
|
234 |
||
235 |
v = {} |
|
236 |
if not product_id: |
|
33
by jf
UF-108: [MERGE] default supp. sugg. |
237 |
v.update({'product_uom_id': False, 'supplier_id': False}) |
1
by jf
Initial Import |
238 |
else: |
239 |
product = product_obj.browse(cr, uid, product_id, context=context) |
|
33
by jf
UF-108: [MERGE] default supp. sugg. |
240 |
v.update({'product_uom_id': product.uom_id.id, 'supplier_id': product.seller_id.id}) |
1
by jf
Initial Import |
241 |
|
242 |
return {'value': v} |
|
33
by jf
UF-108: [MERGE] default supp. sugg. |
243 |
|
244 |
||
245 |
def split_line(self, cr, uid, ids, context={}): |
|
246 |
'''
|
|
247 |
Split a line into two lines
|
|
248 |
'''
|
|
249 |
if ids and isinstance(ids, (int, long)): |
|
250 |
ids = [ids] |
|
251 |
for line in self.browse(cr, uid, ids): |
|
252 |
state = line.list_id.state |
|
253 |
context.update({'line_id': ids[0], 'state': state}) |
|
254 |
return {'type': 'ir.actions.act_window', |
|
255 |
'res_model': 'procurement.list.line.split', |
|
256 |
'target': 'new', |
|
257 |
'view_type': 'form', |
|
258 |
'view_mode': 'form', |
|
259 |
'context': context, |
|
260 |
}
|
|
261 |
||
262 |
def merge_line(self, cr, uid, ids, context={}): |
|
263 |
'''
|
|
264 |
Merges two lines
|
|
265 |
'''
|
|
266 |
if ids and isinstance(ids, (int, long)): |
|
267 |
ids = [ids] |
|
268 |
for line in self.browse(cr, uid, ids): |
|
269 |
state = line.list_id.state |
|
270 |
context.update({'line_id': ids[0], 'state': state}) |
|
271 |
||
272 |
return {'type': 'ir.actions.act_window', |
|
273 |
'res_model': 'procurement.list.line.merge', |
|
274 |
'target': 'new', |
|
275 |
'view_type': 'form', |
|
276 |
'view_mode': 'form', |
|
277 |
'context': context, |
|
278 |
}
|
|
1
by jf
Initial Import |
279 |
|
280 |
procurement_list_line() |
|
281 |
||
282 |
||
283 |
class purchase_order_line(osv.osv): |
|
284 |
_name = 'purchase.order.line' |
|
285 |
_inherit = 'purchase.order.line' |
|
286 |
||
287 |
_columns = { |
|
288 |
'procurement_line_id': fields.many2one('procurement.list.line', string='Procurement Line', readonly=True, ondelete='set null'), |
|
289 |
}
|
|
290 |
||
291 |
def action_confirm(self, cr, uid, ids, context={}): |
|
292 |
'''
|
|
293 |
Changes the status of the procurement line
|
|
294 |
'''
|
|
295 |
proc_line_obj = self.pool.get('procurement.list.line') |
|
296 |
for line in self.browse(cr, uid, ids): |
|
297 |
if line.procurement_line_id and line.procurement_line_id.id: |
|
298 |
proc_line_obj.write(cr, uid, [line.procurement_line_id.id], {'latest': line.order_id.name}) |
|
299 |
||
300 |
return super(purchase_order_line, self).action_confirm(cr, uid, ids, context=context) |
|
301 |
||
302 |
purchase_order_line() |
|
303 |
||
304 |
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
305 |