1
# -*- coding: utf-8 -*-
2
##############################################################################
4
# OpenERP, Open Source Management Solution
5
# Copyright (C) 2012 TeMPO Consulting, MSF. All Rights Reserved
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
24
from tools.translate import _
26
from msf_doc_import import GENERIC_MESSAGE
27
# import below commented in utp-1344: becomes useless as the import is done in wizard
28
#from spreadsheet_xml.spreadsheet_xml import SpreadsheetXML
30
#from msf_doc_import import MAX_LINES_NB
31
from spreadsheet_xml.spreadsheet_xml_write import SpreadsheetCreator
32
from msf_doc_import.wizard import TENDER_COLUMNS_HEADER_FOR_IMPORT as columns_header_for_tender_line_import
33
from msf_doc_import import GENERIC_MESSAGE
34
from msf_doc_import.wizard import TENDER_COLUMNS_FOR_IMPORT as columns_for_tender_line_import
37
class tender(osv.osv):
40
def get_bool_values(self, cr, uid, ids, fields, arg, context=None):
42
if isinstance(ids, (int, long)):
44
for obj in self.browse(cr, uid, ids, context=context):
46
if any([item for item in obj.tender_line_ids if item.to_correct_ok]):
51
# The field below were replaced by the wizard_import_fo_line (utp-113)
52
# 'file_to_import': fields.binary(string='File to import',
53
# help="""* You can use the template of the export for the format that you need to use.
54
# * The file should be in XML Spreadsheet 2003 format.
55
# * You can import up to %s lines each time,
56
# else you have to split the lines in several files and import each one by one.
57
# """ % MAX_LINES_NB),
58
'hide_column_error_ok': fields.function(get_bool_values, method=True, type="boolean", string="Show column errors", store=False),
61
def _check_active_product(self, cr, uid, ids, context=None):
63
Check if the tender contains a line with an inactive products
65
inactive_lines = self.pool.get('tender.line').search(cr, uid, [('product_id.active', '=', False),
66
('tender_id', 'in', ids),
67
('tender_id.state', 'not in', ['draft', 'cancel', 'done'])], context=context)
70
plural = len(inactive_lines) == 1 and _('A product has') or _('Some products have')
71
l_plural = len(inactive_lines) == 1 and _('line') or _('lines')
72
raise osv.except_osv(_('Error'), _('%s been inactivated. If you want to generate RfQ from this document you have to remove/correct the line containing those inactive products (see red %s of the document)') % (plural, l_plural))
77
(_check_active_product, "You cannot validate this tender because it contains a line with an inactive product", ['tender_line_ids', 'state'])
80
# UTP-113 THE METHOD BELOW WAS RETAKEN IN THE WIZARD
81
# def import_file(self, cr, uid, ids, context=None):
83
# Import lines from Excel file (in xml)
87
# if isinstance(ids, (int, long)):
89
# product_obj = self.pool.get('product.product')
90
# uom_obj = self.pool.get('product.uom')
91
# obj_data = self.pool.get('ir.model.data')
92
# tender_line_obj = self.pool.get('tender.line')
93
# view_id = obj_data.get_object_reference(cr, uid, 'tender_flow', 'tender_form')[1]
95
# vals['tender_line_ids'] = []
96
# obj = self.browse(cr, uid, ids, context=context)[0]
97
# if not obj.file_to_import:
98
# raise osv.except_osv(_('Error'), _('Nothing to import.'))
99
# fileobj = SpreadsheetXML(xmlstring=base64.decodestring(obj.file_to_import))
100
# # check that the max number of lines is not excedeed
101
# if check_nb_of_lines(fileobj=fileobj):
102
# raise osv.except_osv(_('Warning !'), _("""You can\'t have more than %s lines in your file.""") % MAX_LINES_NB)
104
# rows = fileobj.getRows()
105
# # ignore the first row
114
# 'warning_list': [],
115
# 'to_correct_ok': False,
116
# 'default_code': obj_data.get_object_reference(cr, uid, 'msf_doc_import', 'product_tbd')[1],
117
# 'uom_id': obj_data.get_object_reference(cr, uid, 'msf_doc_import', 'uom_tbd')[1],
121
# col_count = len(row)
123
# raise osv.except_osv(_('Error'), _(""" Tenders should have exactly 6 columns in this order:
124
#Product Code*, Product Description*, Quantity*, Product UoM*, Unit Price*, Delivery Requested Date*"""))
126
# if not check_empty_line(row=row, col_count=col_count):
128
# # for each cell we check the value
129
# # Cell 0: Product Code
131
# p_value = product_value(cr, uid, obj_data=obj_data, product_obj=product_obj, row=row, to_write=to_write, context=context)
132
# to_write.update({'product_id': p_value['default_code'], 'error_list': p_value['error_list']})
136
# qty_value = quantity_value(product_obj=product_obj, row=row, to_write=to_write, context=context)
137
# to_write.update({'qty': qty_value['product_qty'], 'error_list': qty_value['error_list'], 'warning_list': qty_value['warning_list']})
141
# uom_value = compute_uom_value(cr, uid, obj_data=obj_data, product_obj=product_obj, uom_obj=uom_obj, row=row, to_write=to_write, context=context)
142
# to_write.update({'product_uom': uom_value['uom_id'], 'error_list': uom_value['error_list']})
145
# 'to_correct_ok': [True for x in to_write['error_list']], # the lines with to_correct_ok=True will be red
146
# 'text_error': '\n'.join(to_write['error_list']),
147
# 'tender_id': obj.id,
149
# # we check consistency of uom and product values
150
# tender_line_obj.check_data_for_uom(cr, uid, ids, to_write=to_write, context=context)
151
# vals['tender_line_ids'].append((0, 0, to_write))
153
# print "The line num %s in the Excel file got element outside the defined 6 columns" % line_num
155
# # write tender line on tender
156
# context['import_in_progress'] = True
157
# self.write(cr, uid, ids, vals, context=context)
158
# nb_lines_error = self.pool.get('tender.line').search_count(cr, uid, [('to_correct_ok', '=', True),
159
# ('tender_id', '=', ids[0])], context=context)
161
# msg_to_return = get_log_message(to_write=to_write, tender=True, nb_lines_error=nb_lines_error)
163
# self.log(cr, uid, obj.id, _(msg_to_return), context={'view_id': view_id})
166
def wizard_import_tender_line(self, cr, uid, ids, context=None):
168
Launches the wizard to import lines from a file
172
if isinstance(ids, (int, long)):
174
context.update({'active_id': ids[0]})
175
columns_header = [(_(f[0]), f[1]) for f in columns_header_for_tender_line_import]
176
default_template = SpreadsheetCreator('Template of import', columns_header, [])
177
file = base64.encodestring(default_template.get_xml(default_filters=['decode.utf8']))
178
export_id = self.pool.get('wizard.import.tender.line').create(cr, uid, {'file': file,
179
'filename_template': 'template.xls',
180
'message': """%s %s""" % (GENERIC_MESSAGE, ', '.join([_(f) for f in columns_for_tender_line_import]), ),
181
'filename': 'Lines_Not_Imported.xls',
183
'state': 'draft',}, context)
184
return {'type': 'ir.actions.act_window',
185
'res_model': 'wizard.import.tender.line',
193
def check_lines_to_fix(self, cr, uid, ids, context=None):
194
if isinstance(ids, (int, long)):
196
for var in self.browse(cr, uid, ids, context=context):
197
if var.tender_line_ids:
198
for var in var.tender_line_ids:
199
if var.to_correct_ok:
200
raise osv.except_osv(_('Warning !'), _('You still have lines to correct: check the red lines'))
206
class tender_line(osv.osv):
208
override of tender_line class
210
_inherit = 'tender.line'
211
_description = 'Tender Line'
213
def _get_inactive_product(self, cr, uid, ids, field_name, args, context=None):
215
Fill the error message if the product of the line is inactive
218
for line in self.browse(cr, uid, ids, context=context):
219
res[line.id] = {'inactive_product': False,
220
'inactive_error': ''}
221
if line.to_correct_ok:
222
res[line.id].update({'inactive_error': line.text_error})
223
if line.tender_id and line.tender_id.state not in ('cancel', 'done') and line.product_id and not line.product_id.active:
224
res[line.id] = {'inactive_product': True,
225
'inactive_error': _('The product in line is inactive !')}
230
'to_correct_ok': fields.boolean('To correct'),
231
'text_error': fields.text('Errors'),
232
'inactive_product': fields.function(_get_inactive_product, method=True, type='boolean', string='Product is inactive', store=False, multi='inactive'),
233
'inactive_error': fields.function(_get_inactive_product, method=True, type='char', string='Error', store=False, multi='inactive'),
237
'inactive_product': False,
238
'inactive_error': lambda *a: '',
241
def check_data_for_uom(self, cr, uid, ids, *args, **kwargs):
242
context = kwargs['context']
245
if isinstance(ids, (int, long)):
247
obj_data = self.pool.get('ir.model.data')
248
# we take the values that we are going to write in SO line in "to_write"
249
to_write = kwargs['to_write']
250
text_error = to_write['text_error']
251
product_id = to_write['product_id']
252
uom_id = to_write['product_uom']
253
if uom_id and product_id:
254
product_obj = self.pool.get('product.product')
255
uom_obj = self.pool.get('product.uom')
256
product = product_obj.browse(cr, uid, product_id, context=context)
257
uom = uom_obj.browse(cr, uid, uom_id, context=context)
258
if product.uom_id.category_id.id != uom.category_id.id:
259
# this is inspired by onchange_uom in specific_rules>specific_rules.py
260
text_error += _("""The product UOM must be in the same category than the UOM of the product.
261
The category of the UoM of the product is '%s' whereas the category of the UoM you have chosen is '%s'.
262
""") % (product.uom_id.category_id.name, uom.category_id.name)
263
return to_write.update({'text_error': text_error,
264
'to_correct_ok': True})
265
elif not uom_id or uom_id == obj_data.get_object_reference(cr, uid, 'msf_doc_import', 'uom_tbd')[1] and product_id:
266
# we take the default uom of the product
267
product_uom = product.uom_id.id
268
return to_write.update({'product_uom': product_uom})
269
elif not uom_id or uom_id == obj_data.get_object_reference(cr, uid, 'msf_doc_import', 'uom_tbd')[1]:
270
# this is inspired by the on_change in purchase>purchase.py: product_uom_change
271
text_error += _("\n The UoM was not defined so we set the price unit to 0.0.")
272
return to_write.update({'text_error': text_error,
273
'to_correct_ok': True,
274
'price_unit': 0.0, })
276
def onchange_uom(self, cr, uid, ids, product_id, product_uom, product_qty=0.00, context=None):
278
Check if the UoM is convertible to product standard UoM
281
if product_uom and product_id:
282
product_obj = self.pool.get('product.product')
283
uom_obj = self.pool.get('product.uom')
284
product = product_obj.browse(cr, uid, product_id, context=context)
285
uom = uom_obj.browse(cr, uid, product_uom, context=context)
286
if not self.pool.get('uom.tools').check_uom(cr, uid, product_id, product_uom, context):
287
return {'warning': {'title': _('Wrong Product UOM !'),
288
'message': _("You have to select a product UOM in the same category than the purchase UOM of the product")}}
289
return self.onchange_uom_qty(cr, uid, ids, product_uom, product_qty)
291
def write(self, cr, uid, ids, vals, context=None):
292
if isinstance(ids, (int, long)):
296
if not context.get('import_in_progress') and not context.get('button'):
297
obj_data = self.pool.get('ir.model.data')
298
tbd_uom = obj_data.get_object_reference(cr, uid, 'msf_doc_import', 'uom_tbd')[1]
299
tbd_product = obj_data.get_object_reference(cr, uid, 'msf_doc_import', 'product_tbd')[1]
301
if vals.get('product_uom'):
302
if vals.get('product_uom') == tbd_uom:
303
message += _('You have to define a valid UOM, i.e. not "To be define".')
304
if vals.get('product_id'):
305
if vals.get('product_id') == tbd_product:
306
message += _('You have to define a valid product, i.e. not "To be define".')
307
if vals.get('product_uom') and vals.get('product_id'):
308
product_id = vals.get('product_id')
309
product_uom = vals.get('product_uom')
310
res = self.onchange_uom(cr, uid, ids, product_id, product_uom, vals.get('product_qty', 0.00), context)
311
if res and res['warning']:
312
message += res['warning']['message']
314
raise osv.except_osv(_('Warning !'), _(message))
316
vals['to_correct_ok'] = False
317
vals['text_error'] = False
318
return super(tender_line, self).write(cr, uid, ids, vals, context=context)
320
def create(self, cr, uid, vals, context=None):
324
if not context.get('import_in_progress'):
325
if vals.get('product_uom') and vals.get('product_id'):
326
product_id = vals.get('product_id')
327
product_uom = vals.get('product_uom')
328
res = self.onchange_uom(cr, uid, False, product_id, product_uom, context=context)
329
if res and res['warning']:
330
message += res['warning']['message']
332
raise osv.except_osv(_('Warning !'), _(message))
333
return super(tender_line, self).create(cr, uid, vals, context=context)