1
# -*- coding: utf-8 -*-
2
##############################################################################
4
# OpenERP, Open Source Management Solution
5
# Copyright (C) Copyright (C) 2011 MSF, TeMPO Consulting.
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 osv import fields, osv
23
from tools.translate import _
24
import decimal_precision as dp
28
from msf_outgoing import INTEGRITY_STATUS_SELECTION
30
class kit_selection(osv.osv_memory):
34
_name = "kit.selection"
35
_columns = {'product_id': fields.many2one('product.product', string='Kit Product', readonly=True),
36
'kit_id': fields.many2one('composition.kit', string='Theoretical Kit'),
37
'order_line_id_kit_selection': fields.many2one('purchase.order.line', string="Purchase Order Line", readonly=True, required=True),
39
'product_ids_kit_selection': fields.one2many('kit.selection.line', 'wizard_id_kit_selection_line', string='Replacement Products'),
41
'partner_id_kit_selection': fields.related('order_line_id_kit_selection', 'order_id', 'partner_id', string='Partner', type='many2one', relation='res.partner', readonly=True),
42
'pricelist_id_kit_selection': fields.related('order_line_id_kit_selection', 'order_id', 'pricelist_id', string='PriceList', type='many2one', relation='product.pricelist', readonly=True),
43
'warehouse_id_kit_selection': fields.related('order_line_id_kit_selection', 'order_id', 'warehouse_id', string='Warehouse', type='many2one', relation='stock.warehouse', readonly=True),
46
_defaults = {'product_id': lambda s, cr, uid, c: c.get('product_id', False),
47
'order_line_id_kit_selection': lambda s, cr, uid, c: c.get('active_ids') and c.get('active_ids')[0] or False,
50
def import_items(self, cr, uid, ids, context=None):
52
import lines into product_ids_kit_selection
55
line_obj = self.pool.get('kit.selection.line')
56
# purchase order line id
57
pol_ids = context['active_ids']
59
for obj in self.browse(cr, uid, ids, context=context):
61
raise osv.except_osv(_('Warning !'), _('A theoretical version should be selected.'))
62
if obj.kit_id.state != 'completed':
63
raise osv.except_osv(_('Warning !'), _('The theoretical version must be completed.'))
64
for item in obj.kit_id.composition_item_ids:
65
values = {'order_line_id_kit_selection_line': obj.order_line_id_kit_selection.id,
66
'wizard_id_kit_selection_line': obj.id,
67
'product_id_kit_selection_line': item.item_product_id.id,
68
'qty_kit_selection_line': item.item_qty,
69
'uom_id_kit_selection_line': item.item_uom_id.id,
71
line_obj.create(cr, uid, values, context=dict(context, pol_ids=context['active_ids']))
72
return self.pool.get('wizard').open_wizard(cr, uid, pol_ids, type='update', context=context)
74
def validate_lines(self, cr, uid, ids, context=None):
78
- qty > 0.0 (must_be_greater_than_0)
84
prod_obj = self.pool.get('product.product')
85
lot_obj = self.pool.get('stock.production.lot')
87
errors = {'must_be_greater_than_0': False,
88
'price_must_be_greater_than_0': False,
90
for obj in self.browse(cr, uid, ids, context=context):
91
for item in obj.product_ids_kit_selection:
92
# reset the integrity status
93
item.write({'integrity_status': 'empty'}, context=context)
95
if item.qty_kit_selection_line <= 0.0:
97
errors.update(must_be_greater_than_0=True)
98
item.write({'integrity_status': 'must_be_greater_than_0'}, context=context)
100
if item.price_unit_kit_selection_line <= 0.0:
101
# unit price is needed
102
errors.update(price_must_be_greater_than_0=True)
103
item.write({'integrity_status': 'price_must_be_greater_than_0'}, context=context)
104
# check the encountered errors
105
return all([not x for x in errors.values()])
107
def do_de_kitting(self, cr, uid, ids, context=None):
109
create a purchase order line for each kit item and delete the selected kit purchase order line
111
# quick integrity check
112
assert context, 'No context defined, problem on method call'
113
if isinstance(ids, (int, long)):
116
pol_obj = self.pool.get('purchase.order.line')
117
# id of corresponding purchase order line
118
pol_ids = context['active_ids']
119
pol_id = context['active_ids'][0]
120
pol = pol_obj.browse(cr, uid, pol_id, context=context)
121
# integrity constraint
122
integrity_check = self.validate_lines(cr, uid, ids, context=context)
123
if not integrity_check:
124
# the windows must be updated to trigger tree colors
125
return self.pool.get('wizard').open_wizard(cr, uid, pol_ids, type='update', context=context)
127
ctx_keep_info = context.copy()
128
ctx_keep_info['keepDateAndDistrib'] = True
129
for obj in self.browse(cr, uid, ids, context=context):
130
if not len(obj.product_ids_kit_selection):
131
raise osv.except_osv(_('Warning !'), _('Replacement Items must be selected.'))
132
# for each item from the product_ids_kit_selection
133
for item_v in obj.product_ids_kit_selection:
134
# price unit is mandatory
135
if item_v.price_unit_kit_selection_line <= 0.0:
136
raise osv.except_osv(_('Warning !'), _('Unit Price must be specified for each line.'))
137
# selected product_id
138
product_id = item_v.product_id_kit_selection_line.id
140
qty = item_v.qty_kit_selection_line
142
raise osv.except_osv(_('Warning !'), _('Quantity must be greater than 0.0.'))
144
uom_id = item_v.uom_id_kit_selection_line.id
146
price_unit = item_v.price_unit_kit_selection_line
147
# call purchase order line on change function
148
data = self.pool.get('kit.selection.line')._call_pol_on_change(cr, uid, ids, product_id, qty, uom_id, price_unit,
149
type='product_id_change',
150
context=dict(context, pol_ids=context['active_ids']))
152
p_values = {'product_id': product_id,
153
'product_qty': pol.product_qty*qty,
154
'price_unit': item_v.price_unit_kit_selection_line,
155
'product_uom': uom_id,
156
'default_code': data['value']['default_code'],
157
'name': data['value']['name'],
158
'date_planned': pol.date_planned,
159
'confirmed_delivery_date': pol.confirmed_delivery_date,
160
'default_name': data['value']['default_name'],
161
'order_id': pol.order_id.id,
163
'comment': pol.comment,
164
'procurement_id': pol.procurement_id.id,
165
'partner_id': pol.partner_id.id,
166
'company_id': pol.company_id.id,
169
# copy the original purchase order line
170
new_id = pol_obj.copy(cr, uid, pol_id, p_values, context=ctx_keep_info)
171
# new_id = pol_obj.create(cr, uid, p_values, context=context)
174
pol_obj.unlink(cr, uid, [pol_id], context=context)
176
return {'type': 'ir.actions.act_window_close'}
181
class kit_selection_line(osv.osv_memory):
185
_name = 'kit.selection.line'
187
def create(self, cr, uid, vals, context=None):
189
default price unit from pol on_change function
192
pol_obj = self.pool.get('purchase.order.line')
193
# id of corresponding purchase order line
194
pol_id = context.get('active_ids', False) and context['active_ids'][0]
195
if pol_id and ('price_unit_kit_selection_line' not in vals or vals.get('price_unit_kit_selection_line') == 0.0):
196
pol = pol_obj.browse(cr, uid, pol_id, context=context)
197
# selected product_id
198
product_id = vals.get('product_id_kit_selection_line', False)
200
qty = vals.get('qty_kit_selection_line', 0.0)
202
uom_id = vals.get('uom_id_kit_selection_line', False)
204
price_unit = vals.get('price_unit_kit_selection_line', 0.0)
205
# gather default values
206
data = self._call_pol_on_change(cr, uid, context['active_ids'],
207
product_id, qty, uom_id, price_unit, type='product_id_on_change',
208
context=dict(context, pol_ids=context['active_ids']))
209
# update price_unit value
210
vals.update({'price_unit_kit_selection_line': data['value']['price_unit']})
211
return super(kit_selection_line, self).create(cr, uid, vals, context=context)
213
def _call_pol_on_change(self, cr, uid, ids, product_id, qty, uom_id, price_unit, type, context=None):
215
core function from purchase order line
217
def product_id_change(self, cr, uid, ids, pricelist, product, qty, uom,
218
partner_id, date_order=False, fiscal_position=False, date_planned=False,
219
name=False, price_unit=False, notes=False):
221
# quick integrity check
222
assert context, 'No context defined, problem on method call'
223
if isinstance(ids, (int, long)):
227
pol_obj = self.pool.get('purchase.order.line')
228
prod_obj = self.pool.get('product.product')
229
# id of corresponding purchase order line
230
pol_id = context['pol_ids'][0]
231
pol = pol_obj.browse(cr, uid, pol_id, context=context)
232
# pricelist from purchase order
233
pricelist_id = pol.order_id.pricelist_id.id
234
# partner_id from purchase order
235
partner_id = pol.order_id.partner_id.id
236
# date_order from purchase order
237
date_order = pol.order_id.date_order
238
# fiscal_position from purchase order
239
fiscal_position_id = pol.order_id.fiscal_position.id
240
# date_planned from purchase order line
241
date_planned = pol.date_planned
245
name = prod_obj.read(cr, uid, product_id, ['name'], context=context)['name']
249
state = pol.order_id.state
250
# gather default values
251
if type in ['product_id_change', 'product_uom_change']:
252
data = getattr(pol_obj, type)(cr, uid, ids, pricelist=pricelist_id, product=product_id, qty=qty, uom=uom_id,
253
partner_id=partner_id, date_order=date_order, fiscal_position=fiscal_position_id, date_planned=date_planned,
254
name=name, price_unit=price_unit, notes=notes)
255
elif type == 'product_id_on_change':
256
data = getattr(pol_obj, type)(cr, uid, ids, pricelist=pricelist_id, product=product_id,
257
qty=qty, uom=uom_id, partner_id=partner_id, date_order=date_order,
258
fiscal_position=fiscal_position_id, date_planned=date_planned, name=name,
259
price_unit=price_unit, notes=notes, state=state, old_price_unit=False)
262
def on_product_id_change(self, cr, uid, ids, product_id, qty, uom_id, price_unit, context=None):
264
core function from purchase order line
266
def product_id_change(self, cr, uid, ids, pricelist, product, qty, uom,
267
partner_id, date_order=False, fiscal_position=False, date_planned=False,
268
name=False, price_unit=False, notes=False):
270
# quick integrity check
271
assert context, 'No context defined, problem on method call'
272
if isinstance(ids, (int, long)):
276
result = {'value': {'qty_kit_selection_line': 0.0,
277
'uom_id_kit_selection_line': False,
278
'price_unit_kit_selection_line': 0.0}}
279
# gather default values
280
# data = self._call_pol_on_change(cr, uid, ids, product_id, qty, uom_id, price_unit, 'product_id_change', context=context)
281
data = self._call_pol_on_change(cr, uid, ids, product_id, qty, uom_id, price_unit, 'product_id_on_change', context=context)
282
# update result price_unit and default uom
283
result['value'].update({'price_unit_kit_selection_line': 'price_unit' in data['value'] and data['value']['price_unit'] or 0.0,
284
'qty_kit_selection_line': 'product_qty' in data['value'] and data['value']['product_qty'] or 0.0,
285
'uom_id_kit_selection_line': 'product_uom' in data['value'] and data['value']['product_uom'] or False})
289
def on_uom_id_change(self, cr, uid, ids, product_id, qty, uom_id, price_unit, context=None):
291
core function from purchase order line
293
def product_uom_change(self, cr, uid, ids, pricelist, product, qty, uom,
294
partner_id, date_order=False, fiscal_position=False, date_planned=False,
295
name=False, price_unit=False, notes=False):
297
# quick integrity check
298
assert context, 'No context defined, problem on method call'
299
if isinstance(ids, (int, long)):
303
result = {'value': {'qty_kit_selection_line': 0.0,
304
'price_unit_kit_selection_line': 0.0}}
305
# gather default values
306
data = self._call_pol_on_change(cr, uid, ids, product_id, qty, uom_id, price_unit, 'product_uom_change', context=context)
307
# update result price_unit - qty
308
result['value'].update({'price_unit_kit_selection_line': 'price_unit' in data['value'] and data['value']['price_unit'] or 0.0,
309
'qty_kit_selection_line': 'product_qty' in data['value'] and data['value']['product_qty'] or 0.0})
313
_columns = {'integrity_status': fields.selection(string=' ', selection=INTEGRITY_STATUS_SELECTION, readonly=True),
314
'order_line_id_kit_selection_line': fields.many2one('purchase.order.line', string="Purchase Order Line", readonly=True, required=True),
315
'wizard_id_kit_selection_line': fields.many2one('kit.selection', string='Kit Selection wizard'),
317
'product_id_kit_selection_line': fields.many2one('product.product', string='Product', required=True),
318
'qty_kit_selection_line': fields.float(string='Qty', digits_compute=dp.get_precision('Product UoM'), required=True),
319
'uom_id_kit_selection_line': fields.many2one('product.uom', string='UoM', required=True),
320
'price_unit_kit_selection_line': fields.float('Unit Price', required=True, digits_compute=dp.get_precision('Purchase Price')),
323
_defaults = {'integrity_status': 'empty',