Merge lp:~unifield-team/unifield-wm/us-279 into lp:unifield-wm
- us-279
- Merge into trunk
Proposed by
jftempo
Status: | Merged |
---|---|
Merged at revision: | 2718 |
Proposed branch: | lp:~unifield-team/unifield-wm/us-279 |
Merge into: | lp:unifield-wm |
Diff against target: |
987 lines (+915/-2) 9 files modified
specific_rules/__init__.py (+1/-0) specific_rules/__openerp__.py (+1/-0) specific_rules/report/__init__.py (+2/-1) specific_rules/report/report_stock_inventory_view.xml (+12/-0) specific_rules/report/report_unconsistent_stock.py (+39/-0) specific_rules/report/unconsistent_stock_report_xls.mako (+146/-0) specific_rules/security/ir.model.access.csv (+3/-1) specific_rules/unconsistent_stock_report.py (+654/-0) specific_rules/unconsistent_stock_report_view.xml (+57/-0) |
To merge this branch: | bzr merge lp:~unifield-team/unifield-wm/us-279 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
UniField Reviewer Team | Pending | ||
Review via email: mp+283487@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
- 2713. By Quentin THEURET @Amaris
-
US-279 [IMP] Add a cron job to remove reports aged for 2 day
- 2714. By Quentin THEURET @Amaris
-
US-279 [FIX] Bad model used in the record for ir.cron
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'specific_rules/__init__.py' |
2 | --- specific_rules/__init__.py 2012-07-25 08:03:41 +0000 |
3 | +++ specific_rules/__init__.py 2016-01-21 21:12:42 +0000 |
4 | @@ -19,6 +19,7 @@ |
5 | # |
6 | ############################################################################## |
7 | import specific_rules |
8 | +import unconsistent_stock_report |
9 | import stock |
10 | import wizard |
11 | import report |
12 | |
13 | === modified file 'specific_rules/__openerp__.py' |
14 | --- specific_rules/__openerp__.py 2015-03-02 16:18:37 +0000 |
15 | +++ specific_rules/__openerp__.py 2016-01-21 21:12:42 +0000 |
16 | @@ -42,6 +42,7 @@ |
17 | 'stock_view.xml', |
18 | 'wizard/stock_partial_move_view.xml', |
19 | 'report/report_stock_inventory_view.xml', |
20 | + 'unconsistent_stock_report_view.xml', |
21 | 'stock_sequence.xml', |
22 | ], |
23 | 'installable': True, |
24 | |
25 | === modified file 'specific_rules/report/__init__.py' |
26 | --- specific_rules/report/__init__.py 2015-03-02 16:18:37 +0000 |
27 | +++ specific_rules/report/__init__.py 2016-01-21 21:12:42 +0000 |
28 | @@ -19,5 +19,6 @@ |
29 | # |
30 | ############################################################################## |
31 | |
32 | +import report_stock_inventory |
33 | import report_stock_virtual |
34 | -import report_stock_inventory |
35 | +import report_unconsistent_stock |
36 | |
37 | === modified file 'specific_rules/report/report_stock_inventory_view.xml' |
38 | --- specific_rules/report/report_stock_inventory_view.xml 2015-03-03 14:50:59 +0000 |
39 | +++ specific_rules/report/report_stock_inventory_view.xml 2016-01-21 21:12:42 +0000 |
40 | @@ -114,5 +114,17 @@ |
41 | menu="False" |
42 | /> |
43 | |
44 | + <report |
45 | + id="report_unconsistent_stock_xls" |
46 | + string="Unconsistent stock" |
47 | + model="unconsistent.stock.report" |
48 | + name="unconsistent.stock.report_xls" |
49 | + file="specific_rules/report/unconsistent_stock_report_xls.mako" |
50 | + report_type="webkit" |
51 | + header="False" |
52 | + auto="False" |
53 | + menu="False" |
54 | + /> |
55 | + |
56 | </data> |
57 | </openerp> |
58 | |
59 | === added file 'specific_rules/report/report_unconsistent_stock.py' |
60 | --- specific_rules/report/report_unconsistent_stock.py 1970-01-01 00:00:00 +0000 |
61 | +++ specific_rules/report/report_unconsistent_stock.py 2016-01-21 21:12:42 +0000 |
62 | @@ -0,0 +1,39 @@ |
63 | +# -*- coding: utf-8 -*- |
64 | +############################################################################## |
65 | +# |
66 | +# OpenERP, Open Source Management Solution |
67 | +# Copyright (C) 2011 TeMPO Consulting, MSF. All Rights Reserved |
68 | +# |
69 | +# This program is free software: you can redistribute it and/or modify |
70 | +# it under the terms of the GNU Affero General Public License as |
71 | +# published by the Free Software Foundation, either version 3 of the |
72 | +# License, or (at your option) any later version. |
73 | +# |
74 | +# This program is distributed in the hope that it will be useful, |
75 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
76 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
77 | +# GNU Affero General Public License for more details. |
78 | +# |
79 | +# You should have received a copy of the GNU Affero General Public License |
80 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
81 | +# |
82 | +############################################################################## |
83 | + |
84 | +from report import report_sxw |
85 | +from spreadsheet_xml.spreadsheet_xml_write import SpreadsheetReport |
86 | + |
87 | +class unconsistent_stock_report_xls(SpreadsheetReport): |
88 | + def __init__(self, name, table, rml=False, parser=report_sxw.rml_parse, header='external', store=False): |
89 | + super(unconsistent_stock_report_xls, self).__init__(name, table, |
90 | + rml=rml, parser=parser, header=header, store=store) |
91 | + |
92 | + def create(self, cr, uid, ids, data, context=None): |
93 | + a = super(unconsistent_stock_report_xls, self).create(cr, uid, ids, |
94 | + data, context) |
95 | + return (a[0], 'xls') |
96 | + |
97 | +unconsistent_stock_report_xls( |
98 | + 'report.unconsistent.stock.report_xls', |
99 | + 'unconsistent.stock.report', |
100 | + 'addons/specific_rules/report/unconsistent_stock_report_xls.mako', |
101 | + header=False) |
102 | \ No newline at end of file |
103 | |
104 | === added file 'specific_rules/report/unconsistent_stock_report_xls.mako' |
105 | --- specific_rules/report/unconsistent_stock_report_xls.mako 1970-01-01 00:00:00 +0000 |
106 | +++ specific_rules/report/unconsistent_stock_report_xls.mako 2016-01-21 21:12:42 +0000 |
107 | @@ -0,0 +1,146 @@ |
108 | +<?xml version="1.0"?> |
109 | +<?mso-application progid="Excel.Sheet"?> |
110 | +<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" |
111 | + xmlns:o="urn:schemas-microsoft-com:office:office" |
112 | + xmlns:x="urn:schemas-microsoft-com:office:excel" |
113 | + xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" |
114 | + xmlns:html="http://www.w3.org/TR/REC-html40"> |
115 | + <DocumentProperties xmlns="urn:schemas-microsoft-com:office:office"> |
116 | + <Author>MSFUser</Author> |
117 | + <LastAuthor>MSFUser</LastAuthor> |
118 | + <Created>2012-06-18T15:46:09Z</Created> |
119 | + <Company>Medecins Sans Frontieres</Company> |
120 | + <Version>11.9999</Version> |
121 | + </DocumentProperties> |
122 | + <ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel"> |
123 | + <WindowHeight>13170</WindowHeight> |
124 | + <WindowWidth>19020</WindowWidth> |
125 | + <WindowTopX>120</WindowTopX> |
126 | + <WindowTopY>60</WindowTopY> |
127 | + <ProtectStructure>False</ProtectStructure> |
128 | + <ProtectWindows>False</ProtectWindows> |
129 | + </ExcelWorkbook> |
130 | +<Styles> |
131 | + <Style ss:ID="header"> |
132 | + <Alignment ss:Horizontal="Center" ss:Vertical="Center" ss:WrapText="1"/> |
133 | + <Font ss:FontName="Calibri" x:Family="Swiss" ss:Bold="1" ss:Color="#000000"/> |
134 | + <Interior ss:Color="#efefef" ss:Pattern="Solid" /> |
135 | + <Borders> |
136 | + <Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1" /> |
137 | + <Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1" /> |
138 | + <Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1" /> |
139 | + <Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1" /> |
140 | + </Borders> |
141 | + </Style> |
142 | + <Style ss:ID="line"> |
143 | + <Alignment ss:Horizontal="Center" ss:Vertical="Center" ss:WrapText="1"/> |
144 | + <Font ss:FontName="Calibri" x:Family="Swiss" ss:Color="#000000"/> |
145 | + <Borders> |
146 | + <Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1" /> |
147 | + <Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1" /> |
148 | + <Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1" /> |
149 | + <Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1" /> |
150 | + </Borders> |
151 | + </Style> |
152 | + <Style ss:ID="line_left"> |
153 | + <Alignment ss:Horizontal="Left" ss:Vertical="Center" ss:WrapText="1"/> |
154 | + <Font ss:FontName="Calibri" x:Family="Swiss" ss:Color="#000000"/> |
155 | + <Borders> |
156 | + <Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1" /> |
157 | + <Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1" /> |
158 | + <Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1" /> |
159 | + <Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1" /> |
160 | + </Borders> |
161 | + </Style> |
162 | + <Style ss:ID="line_date"> |
163 | + <Alignment ss:Horizontal="Center" ss:Vertical="Center" ss:WrapText="1"/> |
164 | + <Font ss:FontName="Calibri" x:Family="Swiss" ss:Color="#000000"/> |
165 | + <Borders> |
166 | + <Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1" /> |
167 | + <Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1" /> |
168 | + <Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1" /> |
169 | + <Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1" /> |
170 | + </Borders> |
171 | + <NumberFormat ss:Format="Short Date"/> |
172 | + </Style> |
173 | +</Styles> |
174 | + |
175 | +<ss:Worksheet ss:Name="Unconsistent stock"> |
176 | +<Table x:FullColumns="1" x:FullRows="1"> |
177 | + |
178 | + # Product code |
179 | + <Column ss:AutoFitWidth="1" ss:Width="75" /> |
180 | + # Product Description |
181 | + <Column ss:AutoFitWidth="1" ss:Width="200" /> |
182 | + # BN-management |
183 | + <Column ss:AutoFitWidth="1" ss:Width="60" /> |
184 | + # ED-management |
185 | + <Column ss:AutoFitWidth="1" ss:Width="60" /> |
186 | + # BN |
187 | + <Column ss:AutoFitWidth="1" ss:Width="100" /> |
188 | + # ED |
189 | + <Column ss:AutoFitWidth="1" ss:Width="75" /> |
190 | + # Quantity |
191 | + <Column ss:AutoFitWidth="1" ss:Width="75" /> |
192 | + # Location |
193 | + <Column ss:AutoFitWidth="1" ss:Width="120" /> |
194 | + # Document number |
195 | + <Column ss:AutoFitWidth="1" ss:Width="120" /> |
196 | + |
197 | + % for o in objects: |
198 | + <Row ss:AutoFitHeight="1"> |
199 | + <Cell ss:StyleID="header"><Data ss:Type="String">Code</Data></Cell> |
200 | + <Cell ss:StyleID="header"><Data ss:Type="String">Description</Data></Cell> |
201 | + <Cell ss:StyleID="header"><Data ss:Type="String">BN-management</Data></Cell> |
202 | + <Cell ss:StyleID="header"><Data ss:Type="String">ED-management</Data></Cell> |
203 | + <Cell ss:StyleID="header"><Data ss:Type="String">BN</Data></Cell> |
204 | + <Cell ss:StyleID="header"><Data ss:Type="String">ED</Data></Cell> |
205 | + <Cell ss:StyleID="header"><Data ss:Type="String">Quantity</Data></Cell> |
206 | + <Cell ss:StyleID="header"><Data ss:Type="String">Location</Data></Cell> |
207 | + <Cell ss:StyleID="header"><Data ss:Type="String">Document number</Data></Cell> |
208 | + </Row> |
209 | + |
210 | + % for l in o.line_ids: |
211 | + <Row ss:AutoFitHeight="1"> |
212 | + <Cell ss:StyleID="line"><Data ss:Type="String">${l.product_code or ''|x}</Data></Cell> |
213 | + <Cell ss:StyleID="line_left"><Data ss:Type="String">${l.product_id.name or ''|x}</Data></Cell> |
214 | + <Cell ss:StyleID="line"><Data ss:Type="String">${l.product_bn and 'TRUE' or '-'|x}</Data></Cell> |
215 | + <Cell ss:StyleID="line"><Data ss:Type="String">${l.product_ed and 'TRUE' or '-'|x}</Data></Cell> |
216 | + <Cell ss:StyleID="line"><Data ss:Type="String">${l.prodlot_name or '-'|x}</Data></Cell> |
217 | + % if l.expiry_date not in (False, 'False'): |
218 | + <Cell ss:StyleID="line_date"><Data ss:Type="DateTime">${l.expiry_date|n}T00:00:00.000</Data></Cell> |
219 | + % else: |
220 | + <Cell ss:StyleID="line"><Data ss:Type="String">-</Data></Cell> |
221 | + % endif |
222 | + <Cell ss:StyleID="line"><Data ss:Type="Number">${l.quantity|x}</Data></Cell> |
223 | + <Cell ss:StyleID="line"><Data ss:Type="String">${l.location_id.name or '-'|x}</Data></Cell> |
224 | + <Cell ss:StyleID="line"><Data ss:Type="String">${l.document_number or '-'|x}</Data></Cell> |
225 | + </Row> |
226 | + % endfor |
227 | + % endfor |
228 | +</Table> |
229 | +<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel"> |
230 | + <PageSetup> |
231 | + <Layout x:Orientation="Landscape"/> |
232 | + <Header x:Data="&C&"Arial,Bold"&14"/> |
233 | + <Footer x:Data="Page &P of &N"/> |
234 | + </PageSetup> |
235 | + <Print> |
236 | + <ValidPrinterInfo/> |
237 | + <PaperSizeIndex>9</PaperSizeIndex> |
238 | + <HorizontalResolution>600</HorizontalResolution> |
239 | + <VerticalResolution>600</VerticalResolution> |
240 | + </Print> |
241 | + <Selected/> |
242 | + <Panes> |
243 | + <Pane> |
244 | + <Number>3</Number> |
245 | + <ActiveRow>17</ActiveRow> |
246 | + </Pane> |
247 | + </Panes> |
248 | + <ProtectObjects>False</ProtectObjects> |
249 | + <ProtectScenarios>False</ProtectScenarios> |
250 | +</WorksheetOptions> |
251 | +</ss:Worksheet> |
252 | + |
253 | +</Workbook> |
254 | |
255 | === modified file 'specific_rules/security/ir.model.access.csv' |
256 | --- specific_rules/security/ir.model.access.csv 2012-08-17 11:04:13 +0000 |
257 | +++ specific_rules/security/ir.model.access.csv 2016-01-21 21:12:42 +0000 |
258 | @@ -3,4 +3,6 @@ |
259 | "access_initial_inventory_all","initial.stock.inventory.all","model_initial_stock_inventory",,1,1,1,1 |
260 | "access_initial_inventory_line_all","initial.stock.inventory.line.all","model_initial_stock_inventory_line",,1,1,1,1 |
261 | "access_stock_cost_reevaluation_all","stock.cost.reevaluation.all","model_stock_cost_reevaluation",,1,1,1,1 |
262 | -"access_stock_cost_reevaluation_line_all","stock.cost.reevaluation.line.all","model_stock_cost_reevaluation_line",,1,1,1,1 |
263 | \ No newline at end of file |
264 | +"access_stock_cost_reevaluation_line_all","stock.cost.reevaluation.line.all","model_stock_cost_reevaluation_line",,1,1,1,1 |
265 | +"access_unconsistent_stock_report_all","unconsistent.stock.report.all","model_unconsistent_stock_report",,1,1,1,1 |
266 | +"access_unconsistent_stock_report_line_all","unconsistent.stock.report.line.all","model_unconsistent_stock_report_line",,1,1,1,1 |
267 | |
268 | === added file 'specific_rules/unconsistent_stock_report.py' |
269 | --- specific_rules/unconsistent_stock_report.py 1970-01-01 00:00:00 +0000 |
270 | +++ specific_rules/unconsistent_stock_report.py 2016-01-21 21:12:42 +0000 |
271 | @@ -0,0 +1,654 @@ |
272 | +# -*- coding: utf-8 -*- |
273 | +############################################################################## |
274 | +# |
275 | +# OpenERP, Open Source Management Solution |
276 | +# Copyright (C) 2004-2015 TeMPO Consulting, MSF. |
277 | +# |
278 | +# This program is free software: you can redistribute it and/or modify |
279 | +# it under the terms of the GNU Affero General Public License as |
280 | +# published by the Free Software Foundation, either version 3 of the |
281 | +# License, or (at your option) any later version. |
282 | +# |
283 | +# This program is distributed in the hope that it will be useful, |
284 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
285 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
286 | +# GNU Affero General Public License for more details. |
287 | +# |
288 | +# You should have received a copy of the GNU Affero General Public License |
289 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
290 | +# |
291 | +############################################################################## |
292 | + |
293 | +import datetime |
294 | +import time |
295 | + |
296 | +from osv import osv |
297 | +from osv import fields |
298 | + |
299 | + |
300 | +class unconsistent_stock_report(osv.osv): |
301 | + _name = 'unconsistent.stock.report' |
302 | + |
303 | + _columns = { |
304 | + 'name': fields.datetime( |
305 | + string='Name', |
306 | + ), |
307 | + 'line_ids': fields.one2many( |
308 | + 'unconsistent.stock.report.line', |
309 | + 'report_id', |
310 | + string='Lines', |
311 | + ), |
312 | + } |
313 | + |
314 | + _defaults = { |
315 | + 'name': lambda *a: time.strftime('%Y-%m-%d'), |
316 | + } |
317 | + |
318 | + def _get_unconsistent_prodlots(self, cr, uid, bm='f', perishable='f', context=None): |
319 | + """ |
320 | + Returns a list of ID of stock.production.lot that aren't consistent |
321 | + :param cr: Cursor to the database |
322 | + :param uid: ID of the user that calls the method |
323 | + :param bm: Batch management value of the product |
324 | + :param perishable: Perishable value of the product |
325 | + :param context: Context of the call |
326 | + :return: A dictionnary with values to create unconsistent_stock_report_line |
327 | + """ |
328 | + if bm == 't': |
329 | + extra_where = 'AND spl.type = \'internal\'' |
330 | + elif perishable == 't': |
331 | + extra_where = 'AND spl.type != \'internal\'' |
332 | + else: |
333 | + extra_where = 'AND srp.prodlot_id IS NOT NULL' |
334 | + |
335 | + request = ''' |
336 | + SELECT |
337 | + srp.product_id AS product_id, |
338 | + srp.prodlot_id AS prodlot_id, |
339 | + spl.life_date AS expiry_date, |
340 | + SUM(srp.qty) AS quantity, |
341 | + srp.location_id AS location_id |
342 | + FROM |
343 | + stock_report_prodlots srp |
344 | + LEFT JOIN |
345 | + product_product pp |
346 | + ON pp.id = srp.product_id |
347 | + LEFT JOIN |
348 | + stock_production_lot spl |
349 | + ON spl.id = srp.prodlot_id |
350 | + LEFT JOIN |
351 | + stock_location sl |
352 | + ON sl.id = srp.location_id |
353 | + WHERE |
354 | + pp.batch_management = %%s |
355 | + AND |
356 | + pp.perishable = %%s |
357 | + AND |
358 | + sl.usage = 'internal' |
359 | + %s |
360 | + GROUP BY |
361 | + srp.prodlot_id, srp.location_id, srp.product_id, spl.life_date |
362 | + ''' % extra_where |
363 | + cr.execute(request, (bm, perishable)) |
364 | + |
365 | + for r in cr.dictfetchall(): |
366 | + if r['quantity'] > 0.00: |
367 | + yield r |
368 | + |
369 | + raise StopIteration |
370 | + |
371 | + def _get_unconsistent_non_managed_stock(self, cr, uid, context=None): |
372 | + """ |
373 | + Search the list of production lots in stock that are unconsistent |
374 | + :param cr: Cursor to the database |
375 | + :param uid: ID of the user that calls the method |
376 | + :param bm: Batch management value of the product |
377 | + :param perishable: Perishable value of the product |
378 | + :param context: Context of the call |
379 | + :return: A dictionnary with values to create unconsistent_stock_report_line |
380 | + """ |
381 | + request = ''' |
382 | + SELECT |
383 | + rsi.product_id AS product_id, |
384 | + NULL AS prodlot_id, |
385 | + NULL AS expiry_date, |
386 | + SUM(rsi.product_qty) AS quantity, |
387 | + rsi.location_id AS location_id |
388 | + FROM |
389 | + report_stock_inventory rsi |
390 | + LEFT JOIN |
391 | + product_product pp |
392 | + ON pp.id = rsi.product_id |
393 | + WHERE |
394 | + pp.perishable = 't' |
395 | + AND |
396 | + rsi.location_type = 'internal' |
397 | + AND |
398 | + rsi.state = 'done' |
399 | + AND |
400 | + rsi.prodlot_id IS NULL |
401 | + GROUP BY |
402 | + rsi.prodlot_id, rsi.location_id, rsi.product_id, expiry_date |
403 | + ''' |
404 | + cr.execute(request) |
405 | + |
406 | + for r in cr.dictfetchall(): |
407 | + if r['quantity'] > 0.00: |
408 | + yield r |
409 | + |
410 | + raise StopIteration |
411 | + |
412 | + def _get_unconsistent_moves(self, cr, uid, bm='f', perishable='t', context=None): |
413 | + """ |
414 | + Search the stock.move that aren't not consistent with product parameters |
415 | + :param cr: Cursor to the database |
416 | + :param uid: ID of the user that calls the method |
417 | + :param context: Context of the call |
418 | + :return: A dictionnary with values to create unconsistent_stock_report_line |
419 | + """ |
420 | + if bm == 't': |
421 | + extra_where = ' AND (sm.prodlot_id IS NULL OR spl.type = \'internal\')' |
422 | + elif perishable == 't': |
423 | + extra_where = ' AND (sm.prodlot_id IS NULL OR spl.type != \'internal\')' |
424 | + else: |
425 | + extra_where = ' AND sm.prodlot_id IS NOT NULL' |
426 | + |
427 | + request = ''' |
428 | + SELECT |
429 | + sm.product_id AS product_id, |
430 | + sm.product_qty AS quantity, |
431 | + sm.prodlot_id AS prodlot_id, |
432 | + sm.expired_date AS expiry_date, |
433 | + sm.location_id AS location_id, |
434 | + CASE |
435 | + WHEN sp.subtype = 'packing' AND sp.backorder_id IS NOT NULL |
436 | + THEN ship.name |
437 | + ELSE sp.name |
438 | + END AS document_number |
439 | + FROM |
440 | + stock_move sm |
441 | + LEFT JOIN |
442 | + product_product pp |
443 | + ON pp.id = sm.product_id |
444 | + LEFT JOIN |
445 | + stock_production_lot spl |
446 | + ON spl.id = sm.prodlot_id |
447 | + LEFT JOIN |
448 | + stock_picking sp |
449 | + ON sp.id = sm.picking_id |
450 | + LEFT JOIN |
451 | + shipment ship |
452 | + ON ship.id = sp.shipment_id |
453 | + WHERE |
454 | + sm.kit_creation_id_stock_move IS NULL |
455 | + AND |
456 | + sm.state NOT IN ('cancel', 'done') |
457 | + AND |
458 | + sm.picking_id IS NOT NULL |
459 | + AND |
460 | + sp.type != 'in' |
461 | + AND |
462 | + ( |
463 | + (sp.subtype = 'packing' AND sp.state IN ('draft', 'assigned')) |
464 | + OR |
465 | + (sp.subtype != 'packing' AND sp.state NOT IN ('cancel', 'done')) |
466 | + ) |
467 | + AND |
468 | + pp.batch_management = %%s |
469 | + AND |
470 | + pp.perishable = %%s |
471 | + %s |
472 | + ''' % extra_where |
473 | + cr.execute(request, (bm, perishable)) |
474 | + |
475 | + for r in cr.dictfetchall(): |
476 | + if r['quantity'] > 0.00: |
477 | + yield r |
478 | + |
479 | + raise StopIteration |
480 | + |
481 | + def _get_unconsistent_upil(self, cr, uid, model='stock_inventory', context=None): |
482 | + """ |
483 | + Search the list of stock.inventory.line that are unconsistent with product parameters |
484 | + :param cr: Cursor to the database |
485 | + :param uid: ID of the user that calls the method |
486 | + :param context: Context of the call |
487 | + :return: A dictionnary with values to create unconsistent_stock_report_line |
488 | + """ |
489 | + if model == 'stock_inventory': |
490 | + name = 'Phy. Inv.' |
491 | + else: |
492 | + name = 'Ini. Stock' |
493 | + |
494 | + request = ''' |
495 | + SELECT |
496 | + sil.product_id AS product_id, |
497 | + sil.prod_lot_id AS prodlot_id, |
498 | + sil.expiry_date AS expiry_date, |
499 | + SUM(sil.product_qty) AS quantity, |
500 | + '%s ' || si.name AS document_number, |
501 | + sil.location_id AS location_id |
502 | + FROM |
503 | + %s_line sil |
504 | + LEFT JOIN |
505 | + %s si |
506 | + ON si.id = sil.inventory_id |
507 | + LEFT JOIN |
508 | + product_product pp |
509 | + ON pp.id = sil.product_id |
510 | + LEFT JOIN |
511 | + stock_location sl |
512 | + ON sl.id = sil.location_id |
513 | + LEFT JOIN |
514 | + stock_production_lot spl |
515 | + ON spl.id = sil.prod_lot_id |
516 | + WHERE |
517 | + sl.usage = 'internal' |
518 | + AND |
519 | + si.state NOT IN ('done', 'cancel') |
520 | + AND |
521 | + ( |
522 | + (pp.perishable AND NOT pp.batch_management AND (sil.expiry_date IS NULL OR spl.type != 'internal')) |
523 | + OR |
524 | + (pp.batch_management AND (sil.prod_lot_id IS NULL OR spl.type = 'internal')) |
525 | + OR |
526 | + ( |
527 | + NOT (pp.batch_management OR pp.perishable) |
528 | + AND |
529 | + ( |
530 | + sil.prod_lot_id IS NOT NULL |
531 | + OR |
532 | + sil.expiry_date IS NOT NULL |
533 | + ) |
534 | + ) |
535 | + ) |
536 | + GROUP BY |
537 | + sil.product_id, sil.prod_lot_id, sil.expiry_date, sil.location_id, document_number |
538 | + ''' % (name, model, model) |
539 | + cr.execute(request) |
540 | + |
541 | + for r in cr.dictfetchall(): |
542 | + if r['quantity'] > 0.00: |
543 | + yield r |
544 | + |
545 | + raise StopIteration |
546 | + |
547 | + def _get_unconsistent_ucrl(self, cr, uid, context=None): |
548 | + """ |
549 | + Search the list of real.average.consumption.line that are unconsistent with product parameters |
550 | + :param cr: Cursor to the database |
551 | + :param uid: ID of the user that calls the method |
552 | + :param context: Context of the call |
553 | + :return: A dictionnary with values to create unconsistent_stock_report_line |
554 | + """ |
555 | + request = ''' |
556 | + SELECT |
557 | + racl.product_id AS product_id, |
558 | + racl.prodlot_id AS prodlot_id, |
559 | + racl.expiry_date AS expiry_date, |
560 | + SUM(racl.product_qty) AS quantity, |
561 | + 'Cons. report ' || rac.name AS document_number, |
562 | + rac.cons_location_id AS location_id |
563 | + FROM |
564 | + real_average_consumption_line racl |
565 | + LEFT JOIN |
566 | + real_average_consumption rac |
567 | + ON rac.id = racl.rac_id |
568 | + LEFT JOIN |
569 | + product_product pp |
570 | + ON pp.id = racl.product_id |
571 | + LEFT JOIN |
572 | + stock_location sl |
573 | + ON sl.id = rac.cons_location_id |
574 | + LEFT JOIN |
575 | + stock_production_lot spl |
576 | + ON spl.id = racl.prodlot_id |
577 | + WHERE |
578 | + sl.usage = 'internal' |
579 | + AND |
580 | + rac.state NOT IN ('done', 'cancel') |
581 | + AND |
582 | + ( |
583 | + (pp.perishable AND not pp.batch_management AND (racl.expiry_date IS NULL OR spl.type != 'internal')) |
584 | + OR |
585 | + (pp.batch_management AND (racl.prodlot_id IS NULL OR spl.type = 'internal')) |
586 | + OR |
587 | + ( |
588 | + NOT (pp.batch_management OR pp.perishable) |
589 | + AND |
590 | + ( |
591 | + racl.prodlot_id IS NOT NULL |
592 | + OR |
593 | + racl.expiry_date IS NOT NULL |
594 | + ) |
595 | + ) |
596 | + ) |
597 | + GROUP BY |
598 | + racl.product_id, racl.prodlot_id, racl.expiry_date, rac.cons_location_id, document_number |
599 | + ''' |
600 | + cr.execute(request) |
601 | + |
602 | + for r in cr.dictfetchall(): |
603 | + if r['quantity'] > 0.00: |
604 | + yield r |
605 | + |
606 | + raise StopIteration |
607 | + |
608 | + def _get_unconsistent_ukol(self, cr, uid, context=None): |
609 | + """ |
610 | + Search the list of stock.move that are unconsistent with product parameters |
611 | + :param cr: Cursor to the database |
612 | + :param uid: ID of the user that calls the method |
613 | + :param context: Context of the call |
614 | + :return: A dictionnary with values to create unconsistent_stock_report_line |
615 | + """ |
616 | + request = ''' |
617 | + SELECT |
618 | + sm.product_id AS product_id, |
619 | + sm.prodlot_id AS prodlot_id, |
620 | + sm.expired_date AS expiry_date, |
621 | + SUM(sm.product_qty) AS quantity, |
622 | + 'Kitting Order ' || ko.name AS document_number, |
623 | + sm.location_id AS location_id |
624 | + FROM |
625 | + stock_move sm |
626 | + LEFT JOIN |
627 | + kit_creation ko |
628 | + ON ko.id = sm.kit_creation_id_stock_move |
629 | + LEFT JOIN |
630 | + product_product pp |
631 | + ON pp.id = sm.product_id |
632 | + LEFT JOIN |
633 | + stock_location sl |
634 | + ON sl.id = sm.location_id |
635 | + LEFT JOIN |
636 | + stock_production_lot spl |
637 | + ON spl.id = sm.prodlot_id |
638 | + WHERE |
639 | + sl.usage = 'internal' |
640 | + AND |
641 | + ko.state NOT IN ('done', 'cancel') |
642 | + AND |
643 | + ( |
644 | + (pp.perishable AND NOT pp.batch_management AND (sm.expired_date IS NULL OR spl.type != 'internal')) |
645 | + OR |
646 | + (pp.batch_management AND (sm.prodlot_id IS NULL OR spl.type = 'internal')) |
647 | + OR |
648 | + ( |
649 | + NOT (pp.batch_management OR pp.perishable) |
650 | + AND |
651 | + ( |
652 | + sm.prodlot_id IS NOT NULL |
653 | + OR |
654 | + sm.expired_date IS NOT NULL |
655 | + ) |
656 | + ) |
657 | + ) |
658 | + GROUP BY |
659 | + sm.product_id, sm.prodlot_id, sm.expired_date, sm.location_id, document_number |
660 | + ''' |
661 | + cr.execute(request) |
662 | + |
663 | + for r in cr.dictfetchall(): |
664 | + if r['quantity'] > 0.00: |
665 | + yield r |
666 | + |
667 | + raise StopIteration |
668 | + |
669 | + def _get_unconsistent_ucsm(self, cr, uid, context=None): |
670 | + """ |
671 | + Search the list of claim.stock.move that are unconsistent with product parameters |
672 | + :param cr: Cursor to the database |
673 | + :param uid: ID of the user that calls the method |
674 | + :param context: Context of the call |
675 | + :return: A dictionnary with values to create unconsistent_stock_report_line |
676 | + """ |
677 | + request = ''' |
678 | + SELECT |
679 | + cpl.product_id_claim_product_line AS product_id, |
680 | + cpl.lot_id_claim_product_line AS prodlot_id, |
681 | + cpl.expiry_date_claim_product_line AS expiry_date, |
682 | + SUM(cpl.qty_claim_product_line) AS quantity, |
683 | + 'Claim ' || rc.name AS document_number |
684 | + FROM |
685 | + claim_product_line cpl |
686 | + LEFT JOIN |
687 | + return_claim rc |
688 | + ON rc.id = cpl.claim_id_claim_product_line |
689 | + LEFT JOIN |
690 | + product_product pp |
691 | + ON pp.id = cpl.product_id_claim_product_line |
692 | + LEFT JOIN |
693 | + stock_production_lot spl |
694 | + ON spl.id = cpl.lot_id_claim_product_line |
695 | + WHERE |
696 | + ( |
697 | + (pp.perishable AND (cpl.expiry_date_claim_product_line IS NULL OR spl.type != 'internal')) |
698 | + OR |
699 | + (pp.batch_management AND (cpl.lot_id_claim_product_line IS NULL OR spl.type = 'internal')) |
700 | + OR |
701 | + cpl.lot_id_claim_product_line IS NOT NULL |
702 | + OR |
703 | + cpl.expiry_date_claim_product_line IS NOT NULL |
704 | + ) |
705 | + GROUP BY |
706 | + cpl.product_id_claim_product_line, cpl.lot_id_claim_product_line, cpl.expiry_date_claim_product_line, document_number |
707 | + ''' |
708 | + cr.execute(request) |
709 | + |
710 | + for r in cr.dictfetchall(): |
711 | + if r['quantity'] > 0.00: |
712 | + yield r |
713 | + |
714 | + raise StopIteration |
715 | + |
716 | + def generate_report(self, cr, uid, ids, context=None): |
717 | + """ |
718 | + Generate the report. Call the different methods to find unconsistent data and put them in the report. |
719 | + :param cr: Cursor to the database |
720 | + :param uid: ID of the user that calls the method |
721 | + :param context: Context of the call |
722 | + :return: The ID of the new report |
723 | + """ |
724 | + usrl_obj = self.pool.get('unconsistent.stock.report.line') |
725 | + |
726 | + if context is None: |
727 | + context = {} |
728 | + |
729 | + # Remove last report |
730 | + #self.unlink(cr, uid, self.search(cr, uid, [], context=context), context=context) |
731 | + |
732 | + def create_usrl(vals): |
733 | + vals['report_id'] = ids[0] |
734 | + usrl_obj.create(cr, uid, vals, context=context) |
735 | + |
736 | + # UN-CONSISTENT PRODLOTS |
737 | + # Get the un-consistent stock for non tracked products |
738 | + for ulot in self._get_unconsistent_prodlots(cr, uid, bm='f', perishable='f', context=context): |
739 | + create_usrl(ulot) |
740 | + |
741 | + # Get the un-consistent stock for products that are now ED/BM but without batch |
742 | + for ulot in self._get_unconsistent_non_managed_stock(cr, uid, context=context): |
743 | + create_usrl(ulot) |
744 | + |
745 | + # Get the un-consistent stock for ED products |
746 | + for ulot in self._get_unconsistent_prodlots(cr, uid, bm='f', perishable='t', context=context): |
747 | + create_usrl(ulot) |
748 | + |
749 | + # Get the un-consistent stock for batch managed products |
750 | + for ulot in self._get_unconsistent_prodlots(cr, uid, bm='t', perishable='t', context=context): |
751 | + create_usrl(ulot) |
752 | + |
753 | + # UN-CONSISTENT STOCK MOVES |
754 | + # Get the un-consistent stock moves for non tracked products |
755 | + for usm in self._get_unconsistent_moves(cr, uid, bm='f', perishable='f', context=context): |
756 | + create_usrl(usm) |
757 | + |
758 | + # Get the un-consistent stock moves for ED products |
759 | + for usm in self._get_unconsistent_moves(cr, uid, bm='f', perishable='t', context=context): |
760 | + create_usrl(usm) |
761 | + |
762 | + # Get the un-consistent stock moves for batch managed products |
763 | + for usm in self._get_unconsistent_moves(cr, uid, bm='t', perishable='t', context=context): |
764 | + create_usrl(usm) |
765 | + |
766 | + # Get the un-consistent physical inventory lines |
767 | + for upil in self._get_unconsistent_upil(cr, uid, model='stock_inventory', context=context): |
768 | + create_usrl(upil) |
769 | + |
770 | + # Get the un-consistent initial stock inventory lines |
771 | + for upil in self._get_unconsistent_upil(cr, uid, model='initial_stock_inventory', context=context): |
772 | + create_usrl(upil) |
773 | + |
774 | + # Get the un-consistent consumption report lines |
775 | + for ucrl in self._get_unconsistent_ucrl(cr, uid, context=context): |
776 | + create_usrl(ucrl) |
777 | + |
778 | + # Get the un-consistent kitting order lines |
779 | + for ukol in self._get_unconsistent_ukol(cr, uid, context=context): |
780 | + create_usrl(ukol) |
781 | + |
782 | + # Get the un-consistent claim moves |
783 | + for ucsm in self._get_unconsistent_ucsm(cr, uid, context=context): |
784 | + create_usrl(ucsm) |
785 | + |
786 | + ''' |
787 | + Retrieve the data and print the report in Excel format. |
788 | + ''' |
789 | + background_id = self.pool.get('memory.background.report').create(cr, uid, { |
790 | + 'file_name': 'Unconsistent stock report', |
791 | + 'report_name': 'unconsistent.stock.report_xls', |
792 | + }, context=context) |
793 | + context['background_id'] = background_id |
794 | + context['background_time'] = 30 |
795 | + |
796 | + data = {'ids': ids, 'context': context} |
797 | + return { |
798 | + 'type': 'ir.actions.report.xml', |
799 | + 'report_name': 'unconsistent.stock.report_xls', |
800 | + 'datas': data, |
801 | + 'context': context, |
802 | + } |
803 | + |
804 | + def delete_unused_lines(self, cr, uid, ids, context=None): |
805 | + """ |
806 | + Remove the unconsistent.stock.report.line and unconsistent.stock.report |
807 | + with age larger than 2 days. |
808 | + """ |
809 | + if not ids: |
810 | + ids = [] |
811 | + |
812 | + max_age = datetime.datetime.now() - datetime.timedelta(days=2) |
813 | + report_ids = self.search(cr, uid, [ |
814 | + ('name', '<', max_age.strftime('%Y-%m-%d %H:%M:%S')), |
815 | + ], context=context) |
816 | + |
817 | + self.unlink(cr, uid, report_ids, context=context) |
818 | + |
819 | + return True |
820 | + |
821 | +unconsistent_stock_report() |
822 | + |
823 | + |
824 | +class unconsistent_stock_report_line(osv.osv): |
825 | + _name = 'unconsistent.stock.report.line' |
826 | + _order = 'report_id, product_code, prodlot_id, location_name, document_number desc' |
827 | + |
828 | + _columns = { |
829 | + 'report_id': fields.many2one( |
830 | + 'unconsistent.stock.report', |
831 | + string='Report', |
832 | + required=True, |
833 | + ondelete='cascade', |
834 | + ), |
835 | + 'product_id': fields.many2one( |
836 | + 'product.product', |
837 | + string='Product', |
838 | + required=True, |
839 | + ondelete='cascade', |
840 | + ), |
841 | + 'product_code': fields.related( |
842 | + 'product_id', |
843 | + 'default_code', |
844 | + string='Product Code', |
845 | + type='char', |
846 | + size=128, |
847 | + readonly=True, |
848 | + store=True, |
849 | + ), |
850 | + 'product_name': fields.related( |
851 | + 'product_id', |
852 | + 'name', |
853 | + string='Product Name', |
854 | + type='char', |
855 | + size=512, |
856 | + readonly=True, |
857 | + store=True, |
858 | + ), |
859 | + 'product_bn': fields.related( |
860 | + 'product_id', |
861 | + 'batch_management', |
862 | + string='BN management', |
863 | + type='boolean', |
864 | + readonly=True, |
865 | + store=True, |
866 | + ), |
867 | + 'product_ed': fields.related( |
868 | + 'product_id', |
869 | + 'perishable', |
870 | + string='ED management', |
871 | + type='boolean', |
872 | + readonly=True, |
873 | + store=True, |
874 | + ), |
875 | + 'prodlot_id': fields.many2one( |
876 | + 'stock.production.lot', |
877 | + string='BN', |
878 | + readonly=True, |
879 | + ondelete='cascade', |
880 | + ), |
881 | + 'prodlot_name': fields.related( |
882 | + 'prodlot_id', |
883 | + 'name', |
884 | + string='BN', |
885 | + type='char', |
886 | + size=64, |
887 | + readonly=True, |
888 | + store=True, |
889 | + ), |
890 | + 'expiry_date': fields.related( |
891 | + 'prodlot_id', |
892 | + 'life_date', |
893 | + type='date', |
894 | + string='ED', |
895 | + readonly=True, |
896 | + store=True, |
897 | + ), |
898 | + 'quantity': fields.float( |
899 | + digits=(16,2), |
900 | + string='Qty', |
901 | + readonly=True, |
902 | + ), |
903 | + 'location_id': fields.many2one( |
904 | + 'stock.location', |
905 | + string='Location', |
906 | + readonly=True, |
907 | + ondelete='cascade', |
908 | + ), |
909 | + 'location_name': fields.related( |
910 | + 'location_id', |
911 | + 'name', |
912 | + type='char', |
913 | + size=128, |
914 | + string='Location', |
915 | + readonly=True, |
916 | + store=True, |
917 | + ), |
918 | + 'document_number': fields.char( |
919 | + size=128, |
920 | + string='Document name', |
921 | + readonly=True, |
922 | + ) |
923 | + } |
924 | + |
925 | +unconsistent_stock_report_line() |
926 | |
927 | === added file 'specific_rules/unconsistent_stock_report_view.xml' |
928 | --- specific_rules/unconsistent_stock_report_view.xml 1970-01-01 00:00:00 +0000 |
929 | +++ specific_rules/unconsistent_stock_report_view.xml 2016-01-21 21:12:42 +0000 |
930 | @@ -0,0 +1,57 @@ |
931 | +<?xml version="1.0" encoding="utf-8"?> |
932 | +<openerp> |
933 | + <data> |
934 | + |
935 | + <!--<record id="unconsistent_stock_report_tree_view" model="ir.ui.view">--> |
936 | + <!--<field name="name">unconsistent.stock.report.tree.view</field>--> |
937 | + <!--<field name="model">unconsistent.stock.report</field>--> |
938 | + <!--<field name="type">tree</field>--> |
939 | + <!--<field name="arch" type="xml">--> |
940 | + <!--<tree string="Stock reports">--> |
941 | + <!--<field name="name" />--> |
942 | + <!--</tree>--> |
943 | + <!--</field>--> |
944 | + <!--</record>--> |
945 | + |
946 | + <record id="unconsistent_stock_report_form_view" model="ir.ui.view"> |
947 | + <field name="name">unconsistent.stock.report.form.view</field> |
948 | + <field name="model">unconsistent.stock.report</field> |
949 | + <field name="type">form</field> |
950 | + <field name="arch" type="xml"> |
951 | + <form string="Product Inconsistencies Report generation"> |
952 | + <field name="name" invisible="1" /> |
953 | + <button name="generate_report" string="Generate report" icon="gtk-execute" type="object" colspan="4" /> |
954 | + </form> |
955 | + </field> |
956 | + </record> |
957 | + |
958 | + <record id="action_unconsistent_stock_report" model="ir.actions.act_window"> |
959 | + <field name="name">Product Inconsistencies Report</field> |
960 | + <field name="res_model">unconsistent.stock.report</field> |
961 | + <field name="view_type">form</field> |
962 | + <field name="view_mode">form</field> |
963 | + <field name="target">new</field> |
964 | + </record> |
965 | + |
966 | + <menuitem |
967 | + id="menu_unconsistent_stock_report" |
968 | + parent="object_query.menu_preferences" |
969 | + sequence="50" |
970 | + action="action_unconsistent_stock_report" /> |
971 | + |
972 | + <!-- Make a cron to remove lines from old reports --> |
973 | + <record forcecreate="True" id="ir_cron_remove_unconsistent_line" model="ir.cron"> |
974 | + <field name="name">Delete Unconsistent report lines</field> |
975 | + <field name="active" eval="True" /> |
976 | + <field name="user_id" ref="base.user_root" /> |
977 | + <field name="interval_number">12</field> |
978 | + <field name="interval_type">hours</field> |
979 | + <field name="numbercall">-1</field> |
980 | + <field name="doall" eval="False" /> |
981 | + <field name="model" eval="'unconsistent.stock.report'" /> |
982 | + <field name="function" eval="'delete_unused_lines'" /> |
983 | + <field name="args" eval="'(False,)'" /> |
984 | + </record> |
985 | + |
986 | + </data> |
987 | +</openerp> |