1
# -*- encoding: latin-1 -*-
2
##############################################################################
4
# Copyright (c) 2010 NaN Projectes de Programari Lliure, S.L. All Rights Reserved.
5
# http://www.NaN-tic.com
7
# WARNING: This program as such is intended to be used by professional
8
# programmers who take the whole responsability of assessing all potential
9
# consequences resulting from its eventual inadequacies and bugs
10
# End users who are looking for a ready-to-use solution with commercial
11
# garantees and support are strongly adviced to contract a Free Software
14
# This program is Free Software; you can redistribute it and/or
15
# modify it under the terms of the GNU General Public License
16
# as published by the Free Software Foundation; either version 2
17
# of the License, or (at your option) any later version.
19
# This program is distributed in the hope that it will be useful,
20
# but WITHOUT ANY WARRANTY; without even the implied warranty of
21
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
# GNU General Public License for more details.
24
# You should have received a copy of the GNU General Public License
25
# along with this program; if not, write to the Free Software
26
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28
##############################################################################
32
from tools.translate import _
33
from osv import fields, osv
36
class qc_proof_method(osv.osv):
38
This model stores a method for doing a test. Examples of methods are: "Eu.Pharm.v.v. (2.2.32)" or "HPLC"
41
_name = 'qc.proof.method'
42
_description = 'Method'
44
'name': fields.char('Name', size=100, required=True ,select="1", translate=True),
45
'active':fields.boolean('Active', select="1"),
48
'active': lambda *a: True,
53
class qc_posible_value(osv.osv):
55
This model stores all possible values of qualitative proof.
58
_name = 'qc.posible.value'
60
'name': fields.char('Name', size=200, required=True, select="1", translate=True),
61
'active':fields.boolean('Active', select="1"),
64
'active': lambda *a: True,
69
class qc_proof( osv.osv ):
71
This model stores proofs which will be part of a test. Proofs are classified between qualitative
72
(such as color) and quantitative (such as density).
74
A name_search on thish model will search on 'name' field but also on any of its synonyms.
78
def _synonyms(self, cr, uid, ids, field_name, arg, context=None):
80
for proof in self.browse(cr, uid, ids, context):
82
for syn in proof.synonym_ids:
83
texts.append( syn.name )
84
result[proof.id] = ', '.join( texts )
88
'name': fields.char('Name', size=200, required=True,select="1",translate=True),
89
'ref':fields.char('Code',size=30, select="1"),
90
'active': fields.boolean('Active'),
91
'synonym_ids': fields.one2many('qc.proof.synonym','proof_id','Synonyms'),
92
'type': fields.selection([('qualitative','Qualitative'),('quantitative','Quantitative')], 'Type', select="1",required=True),
93
'posible_values_ids': fields.many2many('qc.posible.value', 'proof_posible_value_rel','posible_value_id','proof_id','Posible Values'),
94
'synonyms': fields.function(_synonyms, method=True, type='char', size='1000', string='Synonyms', store=False),
98
'active': lambda *a: True,
101
def name_search(self, cr, uid, name='', args=None, operator='ilike', context=None, limit=None):
102
result = super(qc_proof,self).name_search(cr, uid, name, args, operator, context, limit)
104
ids = [x[0] for x in result]
106
syns = self.pool.get('qc.proof.synonym').name_search(cr, uid, name, args, operator, context, limit)
107
syns = [x[0] for x in syns]
108
for syn in self.pool.get('qc.proof.synonym').browse(cr, uid, syns, context):
109
if not syn.proof_id.id in ids:
110
new_ids.append( syn.proof_id.id )
111
result += self.name_get(cr, uid, new_ids, context)
114
def name_get(self, cr, uid, ids, context=None):
116
for proof in self.browse(cr, uid, ids, context):
119
text += " [%s]" % proof.synonyms
120
result.append( (proof.id, text) )
126
class qc_proof_synonym(osv.osv):
128
Proofs may have synonyms. These are used because suppliers may use different names for the same
132
_name = 'qc.proof.synonym'
134
'name': fields.char('Name', size=200, required=True, select="1",translate=True),
135
'proof_id':fields.many2one('qc.proof','Proof', required=True ),
140
class qc_test_template_category( osv.osv):
142
This model is used to categorize proof templates.
145
_name = 'qc.test.template.category'
147
def name_get(self, cr, uid, ids, context=None):
150
reads = self.read(cr, uid, ids, ['name','parent_id'], context=context)
153
name = record['name']
154
if record['parent_id']:
155
name = record['parent_id'][1]+' / '+name
156
res.append((record['id'], name))
159
def _name_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None):
160
res = self.name_get(cr, uid, ids, context=context)
163
def _check_recursion(self, cr, uid, ids):
166
cr.execute('SELECT DISTINCT parent_id FROM qc_test_template_category WHERE id IN ('+','.join(map(str,ids))+')')
167
ids = [x[0] for x in cr.fetchall() if x[0] != None]
174
'name': fields.char('Category Name', required=True, size=64, translate=True),
175
'parent_id': fields.many2one('qc.test.template.category', 'Parent Category', select=True),
176
'complete_name': fields.function(_name_get_fnc, method=True, type="char", string='Full Name'),
177
'child_ids': fields.one2many('qc.test.template.category', 'parent_id', 'Child Categories'),
178
'active' : fields.boolean('Active', help="The active field allows you to hide the category without removing it."),
181
(_check_recursion, 'Error ! You can not create recursive categories.', ['parent_id'])
184
'active' : lambda *a: 1,
187
qc_test_template_category()
190
class qc_test_template(osv.osv):
192
A template is a group of proofs to with the values that make them valid.
195
_name = 'qc.test.template'
196
_description='Test Template'
198
def _links_get(self, cr, uid, context=None):
200
obj = self.pool.get('res.request.link')
201
ids = obj.search(cr, uid, [], context=context)
202
res = obj.read(cr, uid, ids, ['object', 'name'], context)
203
return [(r['object'], r['name']) for r in res]
205
def _default_name(self, cr, uid, context=None):
206
if context and context.get('reference_model', False):
207
id = context.get('reference_id')
209
id = context.get('active_id')
211
source=self.pool.get(context['reference_model']).browse(cr, uid, id, context)
212
if hasattr(source, 'name'):
215
def _default_object_id(self, cr, uid, context=None):
216
if context and context.get('reference_model', False):
217
return '%s,%d' % (context['reference_model'], context['reference_id'])
221
def _default_type(self, cr, uid, context=None):
222
if context and context.get('reference_model'):
228
'active':fields.boolean('Active', select="1"),
229
'name': fields.char('Name', size=200, required=True,translate=True,select="1"),
230
'test_template_line_ids':fields.one2many('qc.test.template.line','test_template_id', 'Lines' ),
231
'object_id':fields.reference('Reference Object', selection=_links_get, size=128 ) ,
232
'fill_correct_values':fields.boolean('Fill With Correct Values' ),
233
'type':fields.selection([('generic','Generic'),('related','Related')], 'Type' ),
234
'category_id': fields.many2one('qc.test.template.category','Category'),
238
'name': _default_name,
239
'active': lambda *a: True,
240
'object_id': _default_object_id,
241
'type': _default_type,
246
class qc_test_template_line(osv.osv):
248
Each test template line has a reference to a proof and the valid value/values.
251
_name = 'qc.test.template.line'
252
_order= 'sequence asc'
254
def onchange_proof_id(self, cr, uid, ids, proof_id,context=None):
257
proof = self.pool.get('qc.proof').browse(cr,uid,proof_id)
258
return {'value':{'type':proof.type}}
261
'sequence':fields.integer('Sequence', required=True),
262
'test_template_id': fields.many2one('qc.test.template', 'Test Template'),
263
'proof_id': fields.many2one('qc.proof', 'Proof', required=True),
264
'method_id': fields.many2one('qc.proof.method','Method'),
265
'valid_value': fields.many2one('qc.posible.value','Valid Value'), # Only if qualitative
266
'notes': fields.text('Notes'),
267
'min_value': fields.float('Min'), # Only if quantitative
268
'max_value': fields.float('Max'), # Only if quantitative
269
'uom_id': fields.many2one('product.uom','Uom'), # Only if quantitative
270
'type': fields.selection([('qualitative','Qualitative'),('quantitative','Quantitative')], 'Type', readonly=True),
274
'sequence':lambda *b:1,
277
qc_test_template_line()
280
class qc_test(osv.osv):
282
This model contains an instance of a test template.
287
def _success(self, cr, uid, ids, field_name, arg, context=None):
289
for test in self.browse(cr, uid, ids, context):
291
for line in test.test_line_ids:
295
result[test.id] = success
298
def _links_get(self, cr, uid, context=None):
299
# TODO: Select models
300
obj = self.pool.get('res.request.link')
301
ids = obj.search(cr, uid, [], context=context)
302
res = obj.read(cr, uid, ids, ['object', 'name'], context)
303
return [(r['object'], r['name']) for r in res]
305
def _default_object_id(self, cr, uid, context=None):
306
if context and context.get('reference_model', False):
307
return '%s,%d' % (context['reference_model'], context['reference_id'])
312
'name': fields.datetime('Date',required=True, readonly=True, states={'draft':[('readonly',False)]}),
313
'object_id':fields.reference('Reference', selection=_links_get, size=128, readonly=True, states={'draft':[('readonly',False)]}),
314
'test_template_id': fields.many2one('qc.test.template','Test', states={'success':[('readonly',True)], 'failed':[('readonly',True)]}),
315
'test_line_ids': fields.one2many( 'qc.test.line', 'test_id', 'Test Lines', states={'success':[('readonly',True)], 'failed':[('readonly',True)]}),
316
'test_internal_note': fields.text('Internal Note', states={'success':[('readonly',True)], 'failed':[('readonly',True)]}),
317
'test_external_note': fields.text('External Note', states={'success':[('readonly',True)], 'failed':[('readonly',True)]}),
318
'state': fields.selection([
320
('waiting','Waiting Supervisor Approval'),
321
('success','Quality Success'),
322
('failed','Quality Failed'),
323
], 'State', readonly=True),
324
'success': fields.function(_success, method=True, type='boolean', string='Success', help='This field will be active if all tests have succeeded.'),
325
'enabled': fields.boolean('Enabled', readonly=True, help='If a quality control test is not enabled it means it can not be moved from "Quality Success" or "Quality Failed" state.'),
328
'name' : lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
329
'state': lambda *a: 'draft',
330
'object_id': _default_object_id,
331
'enabled': lambda *a: True,
334
def create(self, cr, uid, datas, context=None):
335
if context and context.get('reference_model', False):
336
datas['object_id'] = context['reference_model'] + "," + str(context['reference_id'])
337
return super(osv.osv, self).create(cr, uid, datas, context=context)
339
def qc_test_success(self, cr, uid, ids, context):
340
self.write(cr, uid, ids, {
345
def qc_test_failed(self, cr, uid, ids, context):
346
self.write(cr, uid, ids, {
351
def test_state(self, cr, uid, ids, mode, *args):
354
return not quality_check
355
if mode == 'success':
359
def set_test_template(self, cr, uid, ids, template_id, context):
361
self.pool.get('qc.test').write( cr, uid, id, {
362
'test_template_id' : template_id
365
test = self.pool.get('qc.test').browse( cr, uid, id, context )
367
if len(test.test_line_ids) > 0:
368
self.pool.get( 'qc.test.line' ).unlink(cr, uid, [x.id for x in test.test_line_ids], context )
370
for line in test.test_template_id.test_template_line_ids:
371
test_line_id = self.pool.get('qc.test.line').create( cr,uid, {
373
'method_id': line.method_id.id,
374
'valid_value': line.valid_value.id,
375
'proof_id': line.proof_id.id,
376
'test_template_line_id': line.id,
378
'min_value': line.min_value,
379
'max_value': line.max_value,
380
'uom_id': line.uom_id.id,
381
'test_uom_id': line.uom_id.id,
382
'proof_type': line.proof_id.type,
387
class qc_test_line(osv.osv):
389
Each test line has a value and a reference to a proof template line.
392
_name = 'qc.test.line'
393
_rec_name = 'proof_id'
395
def quality_test_check( self, cr, uid, ids,field_name, field_value, context ):
397
lines = self.browse(cr,uid,ids,context )
399
if line.proof_type =='qualitative':
400
res[line.id] = self.quality_test_qualitative_check( cr, uid, line, context)
402
res[line.id] = self.quality_test_quantitative_check( cr, uid, line, context)
405
def quality_test_qualitative_check( self, cr, uid, test_line, context ):
406
if test_line.valid_value == test_line.actual_value_ql:
411
def quality_test_quantitative_check( self, cr, uid, test_line, context ):
412
amount = self.pool.get('product.uom')._compute_qty( cr, uid, test_line.uom_id.id, test_line.actual_value_qt, test_line.test_uom_id.id)
413
if amount >= test_line.min_value and amount <= test_line.max_value:
419
'test_id': fields.many2one('qc.test','Test'),
420
'test_template_line_id':fields.many2one('qc.test.template.line','Test Template Line', readonly=True),
421
'proof_id': fields.many2one('qc.proof','Proof', readonly=True),
422
'method_id': fields.many2one('qc.proof.method','Method', readonly=True),
423
'valid_value': fields.many2one('qc.posible.value','Valid Value', readonly=True, help="Value that should have the result to be valid if it is a qualitative proof."),
424
'actual_value_qt': fields.float('Qt.Value', help="Value of the result if it is a quantitative proof."),
425
'actual_value_ql': fields.many2one('qc.posible.value','Ql.Value', help="Value of the result if it is a qualitative proof."),
426
'notes': fields.text('Notes', readonly=True ),
427
'min_value': fields.float('Min', readonly=True, help="Minimum valid value if it is a quantitative proof."),
428
'max_value': fields.float('Max', readonly=True, help="Maximum valid value if it is a quantitative proof."),
429
'uom_id': fields.many2one('product.uom','Uom', readonly=True, help="UoM for minimum and maximum values if it is a quantitative proof."),
430
'test_uom_id':fields.many2one('product.uom','Uom Test', help="UoM of the value of the result if it is a quantitative proof."),
431
'proof_type': fields.selection([('qualitative','Qualitative'),('quantitative','Quantitative')], 'Proof Type', readonly=True),
432
'success': fields.function( quality_test_check,type='boolean',method=True, string="Success?", select="1"),
437
class qc_test_wizard(osv.osv_memory):
439
This wizard is responsible for setting the proof template for a given test. This
440
will not only fill in the 'test_template_id' field, but will also fill in all lines
441
of the test with the corresponding lines of the template.
444
_name = 'qc.test.set.template.wizard'
446
def _default_test_template_id(self, cr, uid, context):
447
id = context.get('active_id',False)
448
test = self.pool.get('qc.test').browse(cr, uid, id, context)
449
ids = self.pool.get('qc.test.template').search(cr, uid, [('object_id','=',test.object_id)], context=context)
450
return ids and ids[0] or False
453
'test_template_id': fields.many2one('qc.test.template', 'Template'),
456
'test_template_id': _default_test_template_id,
459
def action_create_test(self, cr, uid, ids, context):
460
wizard = self.browse(cr, uid, ids[0], context)
461
self.pool.get('qc.test').set_test_template(cr, uid, [context['active_id']], wizard.test_template_id.id, context)
463
'type': 'ir.actions.act_window_close',
466
def action_cancel(self, cr, uid, ids, context=None):
468
'type': 'ir.actions.act_window_close',
473
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: