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
##############################################################################
23
from osv import fields
25
from tools.translate import _
27
from order_types import ORDER_PRIORITY, ORDER_CATEGORY
29
ORDER_TYPE = [('regular', 'Regular'), ('donation_exp', 'Donation before expiry'),
30
('donation_st', 'Standard donation'), ('loan', 'Loan'),
31
('in_kind', 'In Kind Donation'), ('purchase_list', 'Purchase List'),
32
('direct', 'Direct Purchase Order')]
34
class purchase_order_followup(osv.osv_memory):
35
_name = 'purchase.order.followup'
36
_description = 'Purchase Order Followup'
38
def _shipped_rate(self, cr, uid, line, context=None):
40
Return the shipped rate of a PO line
42
uom_obj = self.pool.get('product.uom')
43
line_value = line.price_subtotal
45
for move in line.move_ids:
46
if move.state == 'done':
47
product_qty = uom_obj._compute_qty(cr, uid, move.product_uom.id, move.product_qty, line.product_uom.id)
48
if move.type == 'out':
49
move_value -= product_qty*move.price_unit
50
elif move.type == 'in':
51
move_value += product_qty*move.price_unit
53
return round((move_value/line_value)*100, 2)
55
def _get_move_state(self, cr, uid, move_state, context=None):
56
return self.pool.get('ir.model.fields').get_selection(cr, uid, 'stock.move', 'state', move_state, context)
58
def update_view(self, cr, uid, ids, context=None):
65
order_id = self.browse(cr, uid, ids, context=context)[0].order_id.id
67
raise osv.except_osv(_('Error'), _('Order followup not found !'))
69
self.unlink(cr, uid, ids, context=context)
71
context.update({'active_id': order_id,
72
'active_ids': [order_id],
75
return self.start_order_followup(cr, uid, ids, context)
77
def close_view(self, cr, uid, ids, context=None):
81
return {'type': 'ir.actions.act_window_close'}
83
def start_order_followup(self, cr, uid, ids, context=None):
86
if not context.get('lang'):
87
context['lang'] = self.pool.get('res.users').read(cr, uid, uid, ['context_lang'])['context_lang']
89
ids = context.get('active_ids',[])
93
raise osv.except_osv(_('Error'), _('No order found !'))
95
raise osv.except_osv(_('Error'),
96
_('You should select one order to follow !'))
98
order_obj = self.pool.get('purchase.order')
99
line_obj = self.pool.get('purchase.order.followup.line')
101
for order in order_obj.browse(cr, uid, ids, context=context):
102
if order.state not in ('approved', 'done', 'confirmed_wait', 'split',
103
'sourced', 'except_picking', 'except_invoice'):
104
raise osv.except_osv(_('Error'),
105
_('You cannot follow a non-confirmed Purchase order !'))
107
followup_id = self.create(cr, uid,
108
{'order_id': order.id}, context=context)
110
order_ids = order_obj.search(cr, uid, [('id', '!=', order.id), ('name', 'like', order.name)], context=context)
113
order_ids = [order.id]
115
for o in order_obj.browse(cr, uid, order_ids, context=context):
116
for line in o.order_line:
117
# If the line has no moves
118
if not line.move_ids:
119
line_data = {'followup_id': followup_id,
120
'order_id': line.order_id and line.order_id.id or False,
124
'move_state': 'No move',
125
'line_name': line.line_number,
126
'line_product_id': line.product_id.id,
127
'line_product_qty': line.product_qty,
128
'line_uom_id': line.product_uom.id,
129
'line_confirmed_date': line.confirmed_delivery_date,
130
'line_shipped_rate': 0.0,
131
'move_product_id': False,
132
'move_product_qty': '',
133
'move_uom_id': False,
134
'move_delivery_date': False,
135
'return_move': False,
137
line_obj.create(cr, uid, line_data, context=context)
145
for move in line.move_ids:
146
if move.type == 'internal':
148
elif move.type == 'out':
149
move_ids6.append(move)
150
elif move.state == 'done':
151
move_ids1.append(move)
152
elif move.product_id.id == line.product_id.id:
153
move_ids2.append(move)
154
elif move.product_uom.id == line.product_uom.id:
155
move_ids3.append(move)
156
elif move.date_expected == line.confirmed_delivery_date:
157
move_ids4.append(move)
159
move_ids4.append(move)
160
for move_ids in [move_ids1, move_ids2, move_ids3, move_ids4, move_ids5, move_ids6]:
161
for move in move_ids:
162
line_shipped_rate = "no-progressbar"
164
line_shipped_rate = self._shipped_rate(cr, uid, line, context)
165
line_data = {'followup_id': followup_id,
166
'order_id': line.order_id and line.order_id.id or False,
169
'picking_id': move.picking_id.id,
170
'move_state': self._get_move_state(cr, uid, move.state, context=context),
171
'line_name': line.line_number,
172
'line_product_id': first_move and line.product_id.id or False,
173
'line_product_qty': first_move and line.product_qty or False,
174
'line_uom_id': first_move and line.product_uom.id or False,
175
'line_confirmed_date': first_move and line.confirmed_delivery_date or False,
176
'line_shipped_rate': line_shipped_rate,
177
'move_product_id': line.product_id.id != move.product_id.id and move.product_id.id or False,
178
'move_product_qty': (line.product_qty != move.product_qty or line.product_id.id != move.product_id.id) and '%.2f' % move.product_qty or '',
179
'move_uom_id': line.product_uom.id != move.product_uom.id and move.product_uom.id or False,
180
'move_delivery_date': line.confirmed_delivery_date != move.date[:10] and move.date or False,
181
'return_move': move.type == 'out',
183
line_obj.create(cr, uid, line_data, context=context)
185
# Unflag the first move
190
view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'purchase_followup', 'purchase_order_followup_split_form_view')[1]
192
view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'purchase_followup', 'purchase_order_followup_form_view')[1]
194
res = {'type': 'ir.actions.act_window',
195
'res_model': 'purchase.order.followup',
198
'view_id': [view_id],
199
'res_id': followup_id,
202
# If update the view, return the view in the same screen
203
if context.get('update'):
204
res.update({'target': 'dummy'})
209
'order_id': fields.many2one('purchase.order', string='Order reference', readonly=True),
210
'supplier_ref': fields.related('order_id', 'partner_ref', string='Supplier Reference', readonly=True, type='char'),
211
'delivery_requested_date': fields.related('order_id', 'delivery_requested_date', string='Delivery requested date', type='date', readonly=True),
212
'delivery_confirmed_date': fields.related('order_id', 'delivery_confirmed_date', string='Delivery confirmed date', type='date', readonly=True),
213
'partner_id': fields.related('order_id', 'partner_id', string='Supplier', type='many2one', relation='res.partner', readonly=True),
214
'partner_ref': fields.related('order_id', 'partner_ref', string='Supplier reference', type='char', readonly=True),
215
'order_type': fields.related('order_id', 'order_type', string='Order Type', type='selection', selection=ORDER_TYPE, readonly=True),
216
'priority': fields.related('order_id', 'priority', string='Priority', type='selection', selection=ORDER_PRIORITY, readonly=True),
217
'categ': fields.related('order_id', 'categ', string='Order Category', type='selection', selection=ORDER_CATEGORY, readonly=True),
218
'line_ids': fields.one2many('purchase.order.followup.line', 'followup_id', readonly=True),
222
purchase_order_followup()
224
class purchase_order_followup_line(osv.osv_memory):
225
_name = 'purchase.order.followup.line'
226
_description = 'Purchase Order Followup Line'
227
_rec_name = 'move_id'
230
'move_id': fields.many2one('stock.move', string='Move'),
231
'order_id': fields.many2one('purchase.order', string='Order'),
232
'line_id': fields.many2one('purchase.order.line', string='Order line'),
233
'followup_id': fields.many2one('purchase.order.followup', string='Follow-up'),
234
'line_name': fields.char(size=64, string='#'),
235
'line_product_id': fields.many2one('product.product', string='Product'),
236
'line_product_qty': fields.char(size=64, string='Qty'),
237
'line_uom_id': fields.many2one('product.uom', string='UoM'),
238
'line_confirmed_date': fields.date(string='Del. Conf. date'),
239
'line_shipped_rate': fields.float(digits=(16,2), string='% of line received'),
240
'picking_id': fields.many2one('stock.picking', string='Incoming shipment'),
241
'move_product_id': fields.many2one('product.product', string='New product'),
242
'move_product_qty': fields.char(size=64, string='New Qty'),
243
'move_uom_id': fields.many2one('product.uom', string='New UoM'),
244
'move_delivery_date': fields.date(string='New Del. date'),
245
'move_state': fields.char(size=64, string='State'),
246
'return_move': fields.boolean(string='Is a return move ?'),
249
def go_to_incoming(self, cr, uid, ids, context=None):
251
Open the associated Incoming shipment
253
for line in self.browse(cr, uid, ids, context=context):
254
if not line.picking_id:
255
raise osv.except_osv(_('Error'), _('This line has no incoming shipment !'))
256
view_id = self.pool.get('stock.picking')._hook_picking_get_view(cr, uid, ids, context=context, pick=line.picking_id)[1]
257
return {'type': 'ir.actions.act_window',
258
'res_model': 'stock.picking',
259
'res_id': line.picking_id.id,
262
'view_mode': 'form,tree',
263
'view_id': [view_id]}
265
purchase_order_followup_line()
267
class purchase_order_followup_from_menu(osv.osv_memory):
268
_name = 'purchase.order.followup.from.menu'
269
_description = 'Purchase order followup menu entry'
272
'order_id': fields.many2one('purchase.order', string='PO reference', required=True),
273
'cust_order_id': fields.many2one('purchase.order', string='Supplier reference', required=True),
274
'incoming_id': fields.many2one('stock.picking', string='Incoming shipment', required=True),
275
'cust_order_id2': fields.many2one('purchase.order', string='Customer Name', required=True),
278
def go_to_followup(self, cr, uid, ids, context=None):
281
new_context = context.copy()
283
for menu in self.browse(cr, uid, ids, context=context):
285
new_ids.append(menu.order_id.id)
286
elif menu.cust_order_id.id:
287
new_ids.append(menu.cust_order_id.id)
288
elif menu.cust_order_id2.id:
289
new_ids.append(menu.cust_order_id2.id)
291
new_ids.append(menu.incoming_id.purchase_id.id)
293
new_context['active_ids'] = new_ids
295
return self.pool.get('purchase.order.followup').start_order_followup(cr, uid, ids, context=new_context)
297
def change_order_id(self, cr, uid, ids, order_id, cust_order_id, incoming_id, cust_order_id2, type='order_id'):
300
if type == 'cust_order_id' and cust_order_id:
301
res.update({'order_id': False, 'incoming_id': False, 'cust_order_id2': False})
302
elif type == 'order_id' and order_id:
303
res.update({'cust_order_id': False, 'incoming_id': False, 'cust_order_id2': False})
304
elif type == 'incoming_id' and incoming_id:
305
res.update({'cust_order_id': False, 'order_id': False, 'cust_order_id2': False})
306
if type == 'cust_order_id2' and cust_order_id2:
307
res.update({'cust_order_id': False, 'order_id': False, 'incoming_id': False})
309
return {'value': res}
311
purchase_order_followup_from_menu()
313
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: