1
# -*- coding: utf-8 -*-
2
##############################################################################
4
# OpenERP, Open Source Management Solution
5
# 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
##############################################################################
23
from osv import fields
25
from tools.translate import _
26
from spreadsheet_xml.spreadsheet_xml_write import SpreadsheetCreator
34
REPLACE_DICT = {'cell': 'Cell',
38
def replace_all(text):
39
for i, j in REPLACE_DICT.iteritems():
40
text = text.replace(i, j)
44
class stock_mission_report(osv.osv):
45
_name = 'stock.mission.report'
46
_description = 'Mission stock report'
48
def _get_local_report(self, cr, uid, ids, field_name, args, context=None):
50
Check if the mission stock report is a local report or not
52
if isinstance(ids, (int, long)):
57
local_instance_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.instance_id.id
59
for report in self.read(cr, uid, ids, ['instance_id'], context=context):
60
res[report['id']] = False
61
if report['instance_id'] \
62
and report['instance_id'][0] == local_instance_id:
63
res[report['id']] = True
67
def _src_local_report(self, cr, uid, obj, name, args, context=None):
69
Returns the local or not report mission according to args
73
local_instance_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.instance_id.id
76
if len(arg) > 2 and arg[0] == 'local_report':
77
if (arg[1] == '=' and arg[2] in ('True', 'true', 't', 1)) or \
78
(arg[1] in ('!=', '<>') and arg[2] in ('False', 'false', 'f', 0)):
79
res.append(('instance_id', '=', local_instance_id))
80
elif (arg[1] == '=' and arg[2] in ('False', 'false', 'f', 0)) or \
81
(arg[1] in ('!=', '<>') and arg[2] in ('True', 'true', 't', 1)):
82
res.append(('instance_id', '!=', local_instance_id))
84
raise osv.except_osv(_('Error'), _('Bad operator'))
89
'name': fields.char(size=128, string='Name', required=True),
90
'instance_id': fields.many2one('msf.instance', string='Instance', required=True),
91
'full_view': fields.boolean(string='Is a full view report ?'),
92
'local_report': fields.function(_get_local_report, fnct_search=_src_local_report,
93
type='boolean', method=True, store=False,
94
string='Is a local report ?', help='If the report is a local report, it will be updated periodically'),
95
'report_line': fields.one2many('stock.mission.report.line', 'mission_report_id', string='Lines'),
96
'last_update': fields.datetime(string='Last update'),
97
'move_ids': fields.many2many('stock.move', 'mission_move_rel', 'mission_id', 'move_id', string='Noves'),
98
'export_ok': fields.boolean(string='Export file possible ?'),
99
'ns_nv_file': fields.binary(string='XML export'),
100
'ns_v_file': fields.binary(string='XML export'),
101
's_nv_file': fields.binary(string='XML export'),
102
's_v_file': fields.binary(string='XML export'),
106
'full_view': lambda *a: False,
110
def create(self, cr, uid, vals, context=None):
112
Create lines at report creation
114
res = super(stock_mission_report, self).create(cr, uid, vals, context=context)
116
local_instance_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.instance_id.id
118
# Not update lines for full view or non local reports
119
if vals.get('instance_id', False) and vals['instance_id'] == local_instance_id and not vals.get('full_view', False):
120
if not context.get('no_update', False):
121
self.update(cr, uid, res, context=context)
125
def background_update(self, cr, uid, ids, context=None):
127
Run the update of local stock report in background
132
threaded_calculation = threading.Thread(target=self.update_newthread, args=(cr, uid, ids, context))
133
threaded_calculation.start()
134
return {'type': 'ir.actions.act_window_close'}
136
def update_newthread(self, cr, uid, ids=[], context=None):
137
# Open a new cursor : Don't forget to close it at the end of method
138
cr = pooler.get_db(cr.dbname).cursor()
139
a = time.strftime('%H-%M-%S')
141
self.update(cr, uid, ids=[], context=None)
146
def update(self, cr, uid, ids=[], context=None):
148
Create lines if new products exist or update the existing lines
153
if isinstance(ids, (int, long)):
156
line_obj = self.pool.get('stock.mission.report.line')
158
report_ids = self.search(cr, uid, [('local_report', '=', True)], context=context)
159
full_report_ids = self.search(cr, uid, [('full_view', '=', True)], context=context)
160
instance_id = self.pool.get('res.users').browse(cr, uid, uid).company_id.instance_id
163
# Create a local report if no exist
164
if not report_ids and context.get('update_mode', False) not in ('update', 'init') and instance_id:
166
c.update({'no_update': True})
167
report_ids = [self.create(cr, uid, {'name': instance_id.name,
168
'instance_id': instance_id.id,
169
'full_view': False}, context=c)]
171
# Create a full view report if no exist
172
if not full_report_ids and context.get('update_mode', False) not in ('update', 'init') and instance_id:
174
c.update({'no_update': True})
175
full_report_ids = [self.create(cr, uid, {'name': 'Full view',
176
'instance_id': instance_id.id,
177
'full_view': True}, context=c)]
181
if context.get('update_full_report'):
182
report_ids = full_report_ids
184
product_ids = self.pool.get('product.product').search(cr, uid, [], context=context)
186
for product in self.pool.get('product.product').read(cr, uid, product_ids, ['product_amc', 'reviewed_consumption'], context=context):
187
product_values.setdefault(product['id'], {})
188
product_values[product['id']].setdefault('product_amc', product['product_amc'])
189
product_values[product['id']].setdefault('reviewed_consumption', product['reviewed_consumption'])
192
# Check in each report if new products are in the database and not in the report
193
for report in self.read(cr, uid, report_ids, ['local_report', 'full_view'], context=context):
194
#self.write(cr, uid, [report.id], {'export_ok': False}, context=context)
195
# Create one line by product
196
cr.execute('''SELECT id FROM product_product
198
SELECT product_id FROM stock_mission_report_line WHERE mission_report_id = %s''' % report['id'])
199
for product in cr.fetchall():
200
line_ids.append(line_obj.create(cr, uid, {'product_id': product, 'mission_report_id': report['id']}, context=context))
202
# Don't update lines for full view or non local reports
203
if not report['local_report']:
206
# Update the update date on report
207
self.write(cr, uid, [report['id']], {'last_update': time.strftime('%Y-%m-%d %H:%M:%S'),
208
'export_ok': False}, context=context)
210
if context.get('update_full_report'):
211
full_view = self.search(cr, uid, [('full_view', '=', True)])
213
line_ids = line_obj.search(cr, uid, [('mission_report_id', 'in', full_view)])
214
line_obj.update_full_view_line(cr, uid, line_ids, context=context)
215
elif not report['full_view']:
217
self.update_lines(cr, uid, [report['id']])
219
self._get_export_csv(cr, uid, report['id'], product_values, context=context)
221
# After update of all normal reports, update the full view report
222
if not context.get('update_full_report'):
224
c.update({'update_full_report': True})
225
self.update(cr, uid, [], context=c)
229
def update_lines(self, cr, uid, ids, context=None):
230
location_obj = self.pool.get('stock.location')
231
data_obj = self.pool.get('ir.model.data')
232
line_obj = self.pool.get('stock.mission.report.line')
233
product_obj = self.pool.get('product.product')
234
# Search considered SLocation
235
stock_location_id = data_obj.get_object_reference(cr, uid, 'stock', 'stock_location_stock')
236
if stock_location_id:
237
stock_location_id = stock_location_id[1]
238
internal_loc = location_obj.search(cr, uid, [('usage', '=', 'internal')], context=context)
239
central_loc = location_obj.search(cr, uid, [('central_location_ok', '=', True)], context=context)
240
cross_loc = location_obj.search(cr, uid, [('cross_docking_location_ok', '=', True)], context=context)
241
stock_loc = location_obj.search(cr, uid, [('location_id', 'child_of', stock_location_id),
242
('id', 'not in', cross_loc),
243
('central_location_ok', '=', False)], context=context)
244
cu_loc = location_obj.search(cr, uid, [('usage', '=', 'internal'), ('location_category', '=', 'consumption_unit')], context=context)
245
secondary_location_id = data_obj.get_object_reference(cr, uid, 'msf_config_locations', 'stock_location_intermediate_client_view')
246
if secondary_location_id:
247
secondary_location_id = secondary_location_id[1]
248
secondary_location_ids = location_obj.search(cr, uid, [('location_id', 'child_of', secondary_location_id)], context=context)
250
cu_loc = location_obj.search(cr, uid, [('location_id', 'child_of', cu_loc)], context=context)
251
central_loc = location_obj.search(cr, uid, [('location_id', 'child_of', central_loc)], context=context)
253
# Check if the instance is a coordination or a project
255
company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id
256
coordo = self.pool.get('msf.instance').search(cr, uid, [('level', '=', 'coordo')], context=context)
257
if company.instance_id.level == 'project' and coordo:
258
coordo_id = self.pool.get('msf.instance').browse(cr, uid, coordo[0], context=context).instance
262
cr.execute('''SELECT m.product_id, m.product_qty, m.product_uom, p.name, m.id
264
LEFT JOIN stock_picking s ON m.picking_id = s.id
265
LEFT JOIN res_partner p ON s.partner_id2 = p.id
266
WHERE s.type = 'in' AND m.state in ('confirmed', 'waiting', 'assigned')''')
268
in_pipe_moves = cr.fetchall()
269
for product_id, qty, uom, partner, move_id in in_pipe_moves:
270
line_id = line_obj.search(cr, uid, [('product_id', '=', product_id),
271
('mission_report_id', '=', id)])
273
line = line_obj.browse(cr, uid, line_id[0])
274
if uom != line.product_id.uom_id.id:
275
qty = self.pool.get('product.uom')._compute_qty(cr, uid, uom, qty, line.product_id.uom_id.id)
277
vals = {'in_pipe_qty': 0.00,
278
'in_pipe_coor_qty': 0.00,
281
vals['in_pipe_qty'] = vals['in_pipe_qty'] + qty
283
if partner == coordo_id:
284
vals['in_pipe_coor_qty'] = vals['in_pipe_coor_qty'] + qty
286
line_obj.write(cr, uid, line.id, vals)
290
SELECT id, product_id, product_uom, product_qty, location_id, location_dest_id
293
AND id not in (SELECT move_id FROM mission_move_rel WHERE mission_id = %s)
297
cr.execute('INSERT INTO mission_move_rel VALUES (%s, %s)' % (id, move[0]))
298
product = product_obj.browse(cr, uid, move[1])
299
line_id = line_obj.search(cr, uid, [('product_id', '=', move[1]),
300
('mission_report_id', '=', id)])
302
line = line_obj.browse(cr, uid, line_id[0])
303
qty = self.pool.get('product.uom')._compute_qty(cr, uid, move[2], move[3], product.uom_id.id)
304
vals = {'internal_qty': line.internal_qty or 0.00,
305
'stock_qty': line.stock_qty or 0.00,
306
'central_qty': line.central_qty or 0.00,
307
'cross_qty': line.cross_qty or 0.00,
308
'secondary_qty': line.secondary_qty or 0.00,
309
'cu_qty': line.cu_qty or 0.00,
312
if move[4] in internal_loc:
313
vals['internal_qty'] = vals['internal_qty'] - qty
314
if move[4] in stock_loc:
315
vals['stock_qty'] = vals['stock_qty'] - qty
316
if move[4] in central_loc:
317
vals['central_qty'] = vals['central_qty'] - qty
318
if move[4] in cross_loc:
319
vals['cross_qty'] = vals['cross_qty'] - qty
320
if move[4] in secondary_location_ids:
321
vals['secondary_qty'] = vals['secondary_qty'] - qty
322
if move[4] in cu_loc:
323
vals['cu_qty'] = vals['cu_qty'] - qty
325
if move[5] in internal_loc:
326
vals['internal_qty'] = vals['internal_qty'] + qty
327
if move[5] in stock_loc:
328
vals['stock_qty'] = vals['stock_qty'] + qty
329
if move[5] in central_loc:
330
vals['central_qty'] = vals['central_qty'] + qty
331
if move[5] in cross_loc:
332
vals['cross_qty'] = vals['cross_qty'] + qty
333
if move[5] in secondary_location_ids:
334
vals['secondary_qty'] = vals['secondary_qty'] + qty
335
if move[5] in cu_loc:
336
vals['cu_qty'] = vals['cu_qty'] + qty
338
vals.update({'internal_val': vals['internal_qty'] * product.standard_price})
339
line_obj.write(cr, uid, line.id, vals)
343
def _get_export_csv(self, cr, uid, ids, product_values, context=None):
345
Get the XML files of the stock mission report.
346
This method generates 4 files (according to option set) :
347
* 1 file with no split of WH and no valuation
348
* 1 file with no split of WH and valuation
349
* 1 file with split of WH and valuation
350
* 1 file with split aof WH and valuation
352
context = context or {}
353
if isinstance(ids, (int, long)):
356
line_obj = self.pool.get('stock.mission.report.line')
358
# ns_nv => No split, no valuation
359
# ns_v => No split, valuation
360
# s_nv => Split, no valuation
361
# s_v => Split, valuation
362
# headers (_(Field name), Field type, Technical field name, ns_nv, ns_v, s_nv, s_v
363
headers = [(_('Reference'), 'string', 'default_code', 1, 1, 1, 1),
364
(_('Name'), 'string', 'name', 1, 1, 1, 1),
365
(_('UoM'), 'string', 'uom_id', 1, 1, 1, 1),
366
(_('Cost price'), 'number', 'cost_price', 0, 1, 0, 1),
367
(_('Func. Cur.'), 'string', 'currency_id', 0, 1, 0, 1),
368
(_('Instance stock'), 'number', 'internal_qty', 1, 1, 1, 1),
369
(_('Instance stock val.'), 'number', 'internal_val', 0, 1, 0, 1),
370
(_('Warehouse stock'), 'number', 'wh_qty', 1, 1, 0, 0),
371
(_('Stock Qty.'), 'number', 'stock_qty', 0, 0, 1, 1),
372
(_('Unallocated Stock Qty.'), 'number', 'central_qty', 0, 0, 1, 1),
373
(_('Cross-Docking Qty.'), 'number', 'cross_qty', 1, 1, 1, 1),
374
(_('Secondary Stock Qty.'), 'number', 'secondary_qty', 1, 1, 1, 1),
375
(_('Internal Cons. Unit Qty.'), 'number', 'cu_qty', 1, 1, 1, 1),
376
(_('AMC'), 'number', 'product_amc', 1, 1, 1, 1),
377
(_('FMC'), 'number', 'reviewed_consumption', 1, 1, 1, 1),
378
(_('In Pipe Qty.'), 'number', 'in_pipe_qty', 1, 1, 1, 1),]
379
fields = ['product_amc', 'reviewed_consumption']
381
def set_data(request, report_id, line, data_name,):
383
cr.execute(request, (report_id, line['id']))
385
product_amc = line['product_id'][0] in product_values and product_values[line['product_id'][0]]['product_amc'] or 0.00
386
reviewed_consumption = line['product_id'][0] in product_values and product_values[line['product_id'][0]]['reviewed_consumption'] or 0.00
387
for r in cr.dictfetchall():
388
data += r[data_name] % (product_amc, reviewed_consumption)
390
logging.getLogger('Mission stock report').warning("""An error is occured when compute the consumption values for product at mission stock report file generation. Data: \n %s""" % data_name)
395
# for report in self.browse(cr, uid, ids, context=context):
396
for report_id in ids:
397
# No split, no valuation
398
ns_nv_headers = [(x[0], x[1]) for x in headers if x[3] == 1]
400
# No split, valuation
401
ns_v_headers = [(x[0], x[1]) for x in headers if x[4] == 1]
403
# Split, no valuation
404
s_nv_headers = [(x[0], x[1]) for x in headers if x[5] == 1]
407
s_v_headers = [(x[0], x[1]) for x in headers if x[6] == 1]
410
# lines_ids = line_obj.search(cr, uid, [('mission_report_id', '=', report_id)], context=context)
413
# for line in line_obj.read(cr, uid, lines_ids, ['product_id'], context=context):
414
# No split, No valuation
416
l.product_id AS product_id,
419
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
420
xmlelement(name Data, xmlattributes('String' as "ss:Type"),
421
replace(l.default_code, '%%', '%%%%'))),
423
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
424
xmlelement(name Data, xmlattributes('String' as "ss:Type"),
425
replace(pt.name, '%%', '%%%%'))),
427
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
428
xmlelement(name Data, xmlattributes('String' as "ss:Type"),
429
replace(pu.name, '%%', '%%%%'))),
431
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
432
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
433
trim(to_char(l.internal_qty, '999999999999.999')))),
435
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
436
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
437
trim(to_char(l.wh_qty, '999999999999.999')))),
439
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
440
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
441
trim(to_char(l.cross_qty, '999999999999.999')))),
443
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
444
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
445
trim(to_char(l.secondary_qty, '999999999999.999')))),
447
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
448
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
449
trim(to_char(l.cu_qty, '999999999999.999')))),
451
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
452
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
455
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
456
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
459
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
460
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
461
trim(to_char(l.in_pipe_qty, '999999999999.999'))))
465
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
466
xmlelement(name Data, xmlattributes('String' as "ss:Type"),
467
replace(l.default_code, '%%', '%%%%'))),
469
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
470
xmlelement(name Data, xmlattributes('String' as "ss:Type"),
471
replace(pt.name, '%%', '%%%%'))),
473
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
474
xmlelement(name Data, xmlattributes('String' as "ss:Type"),
475
replace(pu.name, '%%', '%%%%'))),
477
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
478
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
479
trim(to_char(l.internal_qty, '999999999999.999')))),
481
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
482
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
483
trim(to_char(l.stock_qty, '999999999999.999')))),
485
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
486
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
487
trim(to_char(l.central_qty, '999999999999.999')))),
489
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
490
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
491
trim(to_char(l.cross_qty, '999999999999.999')))),
493
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
494
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
495
trim(to_char(l.secondary_qty, '999999999999.999')))),
497
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
498
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
499
trim(to_char(l.cu_qty, '999999999999.999')))),
501
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
502
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
505
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
506
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
509
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
510
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
511
trim(to_char(l.in_pipe_qty, '999999999999.999'))))
515
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
516
xmlelement(name Data, xmlattributes('String' as "ss:Type"),
517
replace(l.default_code, '%%', '%%%%'))),
519
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
520
xmlelement(name Data, xmlattributes('String' as "ss:Type"),
521
replace(pt.name, '%%', '%%%%'))),
523
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
524
xmlelement(name Data, xmlattributes('String' as "ss:Type"),
525
replace(pu.name, '%%', '%%%%'))),
527
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
528
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
529
trim(to_char(pt.standard_price, '999999999999.999')))),
531
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
532
xmlelement(name Data, xmlattributes('String' as "ss:Type"),
535
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
536
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
537
trim(to_char(l.internal_qty, '999999999999.999')))),
539
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
540
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
541
trim(to_char((l.internal_qty * pt.standard_price), '999999999999.999')))),
543
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
544
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
545
trim(to_char(l.wh_qty, '999999999999.999')))),
547
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
548
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
549
trim(to_char(l.cross_qty, '999999999999.999')))),
551
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
552
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
553
trim(to_char(l.secondary_qty, '999999999999.999')))),
555
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
556
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
557
trim(to_char(l.cu_qty, '999999999999.999')))),
559
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
560
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
563
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
564
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
567
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
568
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
569
trim(to_char(l.in_pipe_qty, '999999999999.999'))))
573
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
574
xmlelement(name Data, xmlattributes('String' as "ss:Type"),
575
replace(l.default_code, '%%', '%%%%'))),
577
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
578
xmlelement(name Data, xmlattributes('String' as "ss:Type"),
579
replace(pt.name, '%%', '%%%%'))),
581
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
582
xmlelement(name Data, xmlattributes('String' as "ss:Type"),
583
replace(pu.name, '%%', '%%%%'))),
585
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
586
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
587
trim(to_char((l.internal_qty * pt.standard_price), '999999999999.999')))),
589
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
590
xmlelement(name Data, xmlattributes('String' as "ss:Type"),
593
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
594
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
595
trim(to_char(l.internal_qty, '999999999999.999')))),
597
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
598
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
599
trim(to_char((l.internal_qty * pt.standard_price), '999999999999.999')))),
601
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
602
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
603
trim(to_char(l.stock_qty, '999999999999.999')))),
605
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
606
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
607
trim(to_char(l.central_qty, '999999999999.999')))),
609
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
610
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
611
trim(to_char(l.cross_qty, '999999999999.999')))),
613
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
614
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
615
trim(to_char(l.secondary_qty, '999999999999.999')))),
617
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
618
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
619
trim(to_char(l.cu_qty, '999999999999.999')))),
621
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
622
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
625
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
626
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
629
xmlelement(name Cell, xmlattributes('ssBorder' as "ss:StyleID"),
630
xmlelement(name Data, xmlattributes('Number' as "ss:Type"),
631
trim(to_char(l.in_pipe_qty, '999999999999.999'))))
633
FROM stock_mission_report_line l
634
LEFT JOIN product_product pp ON l.product_id = pp.id
635
LEFT JOIN product_template pt ON pp.product_tmpl_id = pt.id
636
LEFT JOIN product_uom pu ON pt.uom_id = pu.id
637
LEFT JOIN res_currency rc ON pp.currency_id = rc.id
638
WHERE l.mission_report_id = %s'''
640
cr.execute(request, (report_id, ))
641
res = cr.dictfetchall()
644
product_amc = line['product_id'] in product_values and product_values[line['product_id']]['product_amc'] or 0.00
645
reviewed_consumption = line['product_id'] in product_values and product_values[line['product_id']]['reviewed_consumption'] or 0.00
646
ns_nv_data += replace_all(line['ns_nv_data'] % (product_amc, reviewed_consumption))
647
ns_v_data += replace_all(line['ns_v_data'] % (product_amc, reviewed_consumption))
648
s_nv_data += replace_all(line['s_nv_data'] % (product_amc, reviewed_consumption))
649
s_v_data += replace_all(line['s_v_data'] % (product_amc, reviewed_consumption))
651
logging.getLogger('Mission stock report').warning("""An error is occured when generate the mission stock report file. Data: \n %s""" % line)
653
# No split - No valuation
654
ns_nv_tmpl = SpreadsheetCreator('Template of Mission stock - No split - No valuation', ns_nv_headers, [])
655
ns_nv_split = ns_nv_tmpl.get_xml(default_filters=['decode.utf8']).split('</Row>')
656
ns_nv_split[0] = ns_nv_split[0].replace('ss:ExpandedRowCount="1" ', '')
657
ns_nv_file = base64.encodestring(ns_nv_split[0] + '</Row>' + ns_nv_data + ns_nv_split[1])
663
# No split, valuation
664
ns_v_tmpl = SpreadsheetCreator('Template of Mission stock - No split - No valuation', ns_v_headers, [])
665
ns_v_split = ns_v_tmpl.get_xml(default_filters=['decode.utf8']).split('</Row>')
666
ns_v_split[0] = ns_v_split[0].replace('ss:ExpandedRowCount="1" ', '')
667
ns_v_file = base64.encodestring(ns_v_split[0] + '</Row>' + ns_v_data + ns_v_split[1])
673
# Split, no valuation
674
s_nv_tmpl = SpreadsheetCreator('Template of Mission stock - No split - No valuation', s_nv_headers, [])
675
s_nv_split = s_nv_tmpl.get_xml(default_filters=['decode.utf8']).split('</Row>')
676
s_nv_split[0] = s_nv_split[0].replace('ss:ExpandedRowCount="1" ', '')
677
s_nv_file = base64.encodestring(s_nv_split[0] + '</Row>' + s_nv_data + s_nv_split[1])
684
s_v_tmpl = SpreadsheetCreator('Template of Mission stock - No split - No valuation', s_v_headers, [])
685
s_v_split = s_v_tmpl.get_xml(default_filters=['decode.utf8']).split('</Row>')
686
s_v_split[0] = s_v_split[0].replace('ss:ExpandedRowCount="1" ', '')
687
s_v_file = base64.encodestring(s_v_split[0] + '</Row>' + s_v_data + s_v_split[1])
693
self.write(cr, uid, [report_id], {'ns_nv_file': ns_nv_file,
694
'ns_v_file': ns_v_file,
695
's_nv_file': s_nv_file,
696
's_v_file': s_v_file,
697
'export_ok': True}, context=context)
701
stock_mission_report()
704
class stock_mission_report_line(osv.osv):
705
_name = 'stock.mission.report.line'
706
_description = 'Mission stock report line'
707
_order = 'default_code'
709
def _get_product_type_selection(self, cr, uid, context=None):
710
return self.pool.get('product.template').PRODUCT_TYPE
712
def _get_product_subtype_selection(self, cr, uid, context=None):
713
return self.pool.get('product.template').PRODUCT_SUBTYPE
715
def onChangeSearchNomenclature(self, cr, uid, id, position, type, nomen_manda_0, nomen_manda_1, nomen_manda_2, nomen_manda_3, num=True, context=None):
716
return self.pool.get('product.product').onChangeSearchNomenclature(cr, uid, id, position, type, nomen_manda_0, nomen_manda_1, nomen_manda_2, nomen_manda_3, num=num, context=context)
718
def _get_nomen_s(self, cr, uid, ids, fields, *a, **b):
728
def _search_nomen_s(self, cr, uid, obj, name, args, context=None):
737
el = arg[0].split('_')
739
narg = [('_'.join(el), arg[1], arg[2])]
743
def _get_template(self, cr, uid, ids, context=None):
744
return self.pool.get('stock.mission.report.line').search(cr, uid, [('product_id.product_tmpl_id', 'in', ids)], context=context)
746
def _get_wh_qty(self, cr, uid, ids, field_name, args, context=None):
748
for line in self.browse(cr, uid, ids, context=context):
749
res[line.id] = line.stock_qty + line.central_qty
753
def _get_internal_val(self, cr, uid, ids, field_name, args, context=None):
755
for line in self.browse(cr, uid, ids, context=context):
756
res[line.id] = line.internal_qty * line.cost_price
761
'product_id': fields.many2one('product.product', string='Name', required=True, ondelete="cascade"),
762
'default_code': fields.related('product_id', 'default_code', string='Reference', type='char', size=64, store=True),
763
'old_code': fields.related('product_id', 'old_code', string='Old Code', type='char'),
764
'name': fields.related('product_id', 'name', string='Name', type='char'),
765
'categ_id': fields.related('product_id', 'categ_id', string='Category', type='many2one', relation='product.category',
766
store={'product.template': (_get_template, ['type'], 10),
767
'stock.mission.report.line': (lambda self, cr, uid, ids, c={}: ids, ['product_id'], 10)}),
768
'type': fields.related('product_id', 'type', string='Type', type='selection', selection=_get_product_type_selection,
769
store={'product.template': (_get_template, ['type'], 10),
770
'stock.mission.report.line': (lambda self, cr, uid, ids, c={}: ids, ['product_id'], 10)}),
771
'subtype': fields.related('product_id', 'subtype', string='Subtype', type='selection', selection=_get_product_subtype_selection,
772
store={'product.template': (_get_template, ['subtype'], 10),
773
'stock.mission.report.line': (lambda self, cr, uid, ids, c={}: ids, ['product_id'], 10)}),
774
# mandatory nomenclature levels
775
'nomen_manda_0': fields.related('product_id', 'nomen_manda_0', type='many2one', relation='product.nomenclature', string='Main Type'),
776
'nomen_manda_1': fields.related('product_id', 'nomen_manda_1', type='many2one', relation='product.nomenclature', string='Group'),
777
'nomen_manda_2': fields.related('product_id', 'nomen_manda_2', type='many2one', relation='product.nomenclature', string='Family'),
778
'nomen_manda_3': fields.related('product_id', 'nomen_manda_3', type='many2one', relation='product.nomenclature', string='Root'),
779
# optional nomenclature levels
780
'nomen_sub_0': fields.related('product_id', 'nomen_sub_0', type='many2one', relation='product.nomenclature', string='Sub Class 1'),
781
'nomen_sub_1': fields.related('product_id', 'nomen_sub_1', type='many2one', relation='product.nomenclature', string='Sub Class 2'),
782
'nomen_sub_2': fields.related('product_id', 'nomen_sub_2', type='many2one', relation='product.nomenclature', string='Sub Class 3'),
783
'nomen_sub_3': fields.related('product_id', 'nomen_sub_3', type='many2one', relation='product.nomenclature', string='Sub Class 4'),
784
'nomen_sub_4': fields.related('product_id', 'nomen_sub_4', type='many2one', relation='product.nomenclature', string='Sub Class 5'),
785
'nomen_sub_5': fields.related('product_id', 'nomen_sub_5', type='many2one', relation='product.nomenclature', string='Sub Class 6'),
786
'nomen_manda_0_s': fields.function(_get_nomen_s, method=True, type='many2one', relation='product.nomenclature', string='Main Type', fnct_search=_search_nomen_s, multi="nom_s"),
787
'nomen_manda_1_s': fields.function(_get_nomen_s, method=True, type='many2one', relation='product.nomenclature', string='Group', fnct_search=_search_nomen_s, multi="nom_s"),
788
'nomen_manda_2_s': fields.function(_get_nomen_s, method=True, type='many2one', relation='product.nomenclature', string='Family', fnct_search=_search_nomen_s, multi="nom_s"),
789
'nomen_manda_3_s': fields.function(_get_nomen_s, method=True, type='many2one', relation='product.nomenclature', string='Root', fnct_search=_search_nomen_s, multi="nom_s"),
790
'nomen_sub_0_s': fields.function(_get_nomen_s, method=True, type='many2one', relation='product.nomenclature', string='Sub Class 1', fnct_search=_search_nomen_s, multi="nom_s"),
791
'nomen_sub_1_s': fields.function(_get_nomen_s, method=True, type='many2one', relation='product.nomenclature', string='Sub Class 2', fnct_search=_search_nomen_s, multi="nom_s"),
792
'nomen_sub_2_s': fields.function(_get_nomen_s, method=True, type='many2one', relation='product.nomenclature', string='Sub Class 3', fnct_search=_search_nomen_s, multi="nom_s"),
793
'nomen_sub_3_s': fields.function(_get_nomen_s, method=True, type='many2one', relation='product.nomenclature', string='Sub Class 4', fnct_search=_search_nomen_s, multi="nom_s"),
794
'nomen_sub_4_s': fields.function(_get_nomen_s, method=True, type='many2one', relation='product.nomenclature', string='Sub Class 5', fnct_search=_search_nomen_s, multi="nom_s"),
795
'nomen_sub_5_s': fields.function(_get_nomen_s, method=True, type='many2one', relation='product.nomenclature', string='Sub Class 6', fnct_search=_search_nomen_s, multi="nom_s"),
796
'product_amc': fields.related('product_id', 'product_amc', type='float', string='AMC'),
797
'reviewed_consumption': fields.related('product_id', 'reviewed_consumption', type='float', string='FMC'),
798
'currency_id': fields.related('product_id', 'currency_id', type='many2one', relation='res.currency', string='Func. cur.'),
799
'cost_price': fields.related('product_id', 'standard_price', type='float', string='Cost price'),
800
'uom_id': fields.related('product_id', 'uom_id', type='many2one', relation='product.uom', string='UoM',
801
store={'product.template': (_get_template, ['type'], 10)}),
802
'mission_report_id': fields.many2one('stock.mission.report', string='Mission Report', required=True),
803
'internal_qty': fields.float(digits=(16,2), string='Instance Stock'),
804
'internal_val': fields.function(_get_internal_val, method=True, type='float', string='Instance Stock Val.'),
805
#'internal_val': fields.float(digits=(16,2), string='Instance Stock Val.'),
806
'stock_qty': fields.float(digits=(16,2), string='Stock Qty.'),
807
'stock_val': fields.float(digits=(16,2), string='Stock Val.'),
808
'central_qty': fields.float(digits=(16,2), string='Unallocated Stock Qty.'),
809
'central_val': fields.float(digits=(16,2), string='Unallocated Stock Val.'),
810
'wh_qty': fields.function(_get_wh_qty, method=True, type='float', string='Warehouse stock',
811
store={'stock.mission.report.line': (lambda self, cr, uid, ids, c=None: ids, ['stock_qty', 'central_qty'], 10),}),
812
'cross_qty': fields.float(digits=(16,3), string='Cross-docking Qty.'),
813
'cross_val': fields.float(digits=(16,3), string='Cross-docking Val.'),
814
'secondary_qty': fields.float(digits=(16,2), string='Secondary Stock Qty.'),
815
'secondary_val': fields.float(digits=(16,2), string='Secondary Stock Val.'),
816
'cu_qty': fields.float(digits=(16,2), string='Internal Cons. Unit Qty.'),
817
'cu_val': fields.float(digits=(16,2), string='Internal Cons. Unit Val.'),
818
'in_pipe_qty': fields.float(digits=(16,2), string='In Pipe Qty.'),
819
'in_pipe_val': fields.float(digits=(16,2), string='In Pipe Val.'),
820
'in_pipe_coor_qty': fields.float(digits=(16,2), string='In Pipe from Coord.'),
821
'in_pipe_coor_val': fields.float(digits=(16,2), string='In Pipe from Coord.'),
822
'updated': fields.boolean(string='Updated'),
823
'full_view': fields.related('mission_report_id', 'full_view', string='Full view', type='boolean', store=True),
824
'move_ids': fields.many2many('stock.move', 'mission_line_move_rel', 'line_id', 'move_id', string='Noves'),
828
'internal_qty': 0.00,
829
'internal_val': 0.00,
837
'secondary_qty': 0.00,
838
'secondary_val': 0.00,
843
'in_pipe_coor_qty': 0.00,
844
'in_pipe_coor_val': 0.00,
847
def update_full_view_line(self, cr, uid, ids, context=None):
849
if self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.instance_id.level == 'project':
853
request = '''SELECT l.product_id AS product_id,
854
sum(l.internal_qty) AS internal_qty,
855
sum(l.stock_qty) AS stock_qty,
856
sum(l.central_qty) AS central_qty,
857
sum(l.cross_qty) AS cross_qty,
858
sum(l.secondary_qty) AS secondary_qty,
859
sum(l.cu_qty) AS cu_qty,
860
sum(l.in_pipe_qty) AS in_pipe_qty,
861
sum(l.in_pipe_coor_qty) AS in_pipe_coor_qty,
862
sum(l.internal_qty)*t.standard_price AS internal_val
863
FROM stock_mission_report_line l
865
stock_mission_report m
866
ON l.mission_report_id = m.id
869
ON l.product_id = p.id
872
ON p.product_tmpl_id = t.id
873
WHERE m.full_view = False
874
AND (l.internal_qty != 0.00
875
OR l.stock_qty != 0.00
876
OR l.central_qty != 0.00
877
OR l.cross_qty != 0.00
878
OR l.secondary_qty != 0.00
880
OR l.in_pipe_qty != 0.00
881
OR l.in_pipe_coor_qty != 0.00)
882
GROUP BY l.product_id, t.standard_price'''
887
mission_report_id = self.pool.get('stock.mission.report').search(cr, uid, [('full_view', '=', True)], context=context)
889
line_ids = self.search(cr, uid, [('mission_report_id.full_view', '=', True), ('product_id', '=', line[0])], context=context)
891
if not mission_report_id:
893
line_id = self.create(cr, uid, {'mission_report_id': mission_report_id[0],
894
'product_id': line[0]}, context=context)
896
line_id = line_ids[0]
898
in_pipe = line[7] or 0.00
900
in_pipe = (line[7] or 0.00) - (line[8] or 0.00)
902
self.write(cr, uid, [line_id], {'internal_qty': line[1] or 0.00,
903
'internal_val': line[9] or 0.00,
904
'stock_qty': line[2] or 0.00,
905
'central_qty': line[3] or 0.00,
906
'cross_qty': line[4] or 0.00,
907
'secondary_qty': line[5] or 0.00,
908
'cu_qty': line[6] or 0.00,
909
'in_pipe_qty': line[7] or 0.00,
910
'in_pipe_coor_qty': line[8] or 0.00,}, context=context)
914
stock_mission_report_line()