stock.py
11.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# -*- coding: utf-8 -*-
##############################################################################
#
# Purchase - Computed Purchase Order Module for Odoo
# Copyright (C) 2013-Today GRAP (http://www.grap.coop)
# @author Julien WESTE
# @author Sylvain LE GAL (https://twitter.com/legalsylvain)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import datetime
from openerp.osv import fields, osv
from openerp.tools.translate import _
import openerp.addons.decimal_precision as dp
import logging
_logger = logging.getLogger(__name__)
class StockInventory(osv.osv):
_inherit = "stock.inventory"
def _get_number_week(self, cr, uid, ids, date, args, context=None):
_logger.info('------------------ _get_number_week -------------------')
res = {}
for inv in self.browse(cr, uid, ids, context=context):
week_number = datetime.datetime.strptime(inv.date, "%Y-%m-%d %H:%M:%S").strftime("%W")
res[inv.id] = week_number
return res
_columns = {
'week_number': fields.integer(string="Week num.", readonly=True, help="Number of the week in the current year"),
'week_date': fields.date(string="Began order schuduling on.", help="Week planning start date"),
'hide_initialisation': fields.boolean(string="Hide initialisation area", help="Hide Init. area",
states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}),
'categ_ids': fields.many2many('product.category', 'stock_inventory_product_categ', 'inventory_id', 'categ_id',
'Product categories',
states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}),
'supplier_ids': fields.many2many('res.partner', 'stock_inventory_res_partner', 'inventory_id', 'supplier_id',
'Suppliers', domain=[('supplier', '=', True), ('is_company', '=', True)],
help="Specify product category to focus in your inventory.",
states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}),
'weekly_inventory': fields.boolean(string="Is a weekly inventory",
help="Technical field to distinct odoo inventory with weekkly inventory ",
states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}),
}
def onchange_date(self, cr, uid, ids, date, context=None):
res = {}
if date:
week_number = datetime.datetime.strptime(date, "%Y-%m-%d %H:%M:%S").strftime("%W")
res['value'] = {'week_number': week_number}
return res
def _coop_produce_get_inventory_lines(self, cr, uid, inventory, context=None):
'''
Function inspired from stock/stock.py to add categories filters
and adds categ_ids, suppliers to the filter
'''
location_obj = self.pool.get('stock.location')
product_obj = self.pool.get('product.product')
supplierinfo_obj = self.pool.get('product.supplierinfo')
location_ids = location_obj.search(cr, uid, [('id', 'child_of', [inventory.location_id.id])], context=context)
domain = ' sq.location_id in %s'
product_ids = []
vals = []
args = (tuple(location_ids),)
if inventory.company_id.id:
domain += ' and sq.company_id = %s'
args += (inventory.company_id.id,)
# Look for already existing product
already_added_product_ids = [line.product_id.id for line in inventory.line_ids]
if inventory.categ_ids:
domain += ' and product_id in %s'
categ_ids = [x.id for x in inventory.categ_ids]
# Look for products belong to selectec categories
product_ids = product_obj.search(cr, uid, [('categ_id', 'in', categ_ids)], context=context)
if inventory.supplier_ids:
domain += ' and product_id in %s'
supplier_ids = [x.id for x in inventory.supplier_ids]
# Look for already existing product
already_added_product_ids = [line.product_id.id for line in inventory.line_ids]
# Look for products belong to selectec categories
supplierinfo_ids = supplierinfo_obj.search(cr, uid, [('name', 'in', supplier_ids)], context=context)
product_tmpls = supplierinfo_obj.read(cr, uid, supplierinfo_ids, ['product_tmpl_id'])
product_tmpl_ids = [x['product_tmpl_id'][0] for x in product_tmpls if x]
product_ids += product_obj.search(cr, uid, [('product_tmpl_id', 'in', product_tmpl_ids)], context=context)
# treate only new poducts to add and no modification to existing products
product_ids = set(product_ids) ^ set(already_added_product_ids)
if not product_ids:
return vals
else:
args += (tuple(product_ids),)
query = '''
SELECT sq.product_id as product_id,
pt.default_packaging as default_packaging,
pt.uom_id as product_uom_id,
sum(sq.qty) as product_qty,
sum(sq.qty)/COALESCE(pt.default_packaging,1.0) as packaging_qty,
sq.location_id as location_id
FROM stock_quant sq
INNER JOIN product_product pp ON ( sq.product_id = pp.id)
INNER JOIN product_template pt ON ( pp.product_tmpl_id = pt.id)
WHERE''' + domain + '''
GROUP BY sq.product_id, pt.default_packaging, pt.uom_id,sq.location_id
'''
cr.execute(query, args)
found_product_ids = []
for product_line in cr.dictfetchall():
product_line['inventory_id'] = inventory.id
product_line['product_qty'] = 0.0 #Reset quantity to force the user to set the quantity manually
vals.append(product_line)
found_product_ids.append(product_line['product_id'])
unfound_product_ids = list(set(product_ids) - set(found_product_ids))
for product in product_obj.browse(cr, uid, unfound_product_ids, context):
vals.append({
'product_id': product.id,
'default_packaging': product.default_packaging,
'product_uom_id': product.uom_id.id,
'packaging_qty': 0.0,
'product_qty':0.0,
'location_id': inventory.location_id.id,
'inventory_id': inventory.id,
})
return vals
def action_add_category_supplier(self, cr, uid, ids, context=None):
_logger.info('------------------ action_add_category -------------------')
if not ids:
raise osv.except_osv(('Error'),
("Please select an inventory to continue"))
if len(ids) > 1:
raise osv.except_osv(('Error'),
("You should on one inventory in the same time"))
inventory_line_obj = self.pool.get('stock.inventory.line')
inv = self.browse(cr, uid, ids[0], context=context)
lines = self._coop_produce_get_inventory_lines(cr, uid, inv, context=context)
for line in lines:
inventory_line_obj.create(cr, uid, line, context=context)
return True
def action_reset(self, cr, uid, ids, context=None):
for inv in self.browse(cr, uid, ids, context=context):
inv.line_ids.unlink()
def init_with_theorical_qty(self,cr,uid,ids,context=None):
for inv in self.browse(cr, uid, ids, context=context):
for line in inv.line_ids:
line.write({'product_qty':line.packaging_qty*line.default_packaging,
'stock_qty':line.packaging_qty})
class StockInventoryLine(osv.osv):
_inherit = "stock.inventory.line"
def _get_qty_loss(self, cr, uid, ids, name, args, context=None):
res = {}
for line in self.browse(cr, uid, ids, context=context):
res[line.id] = line.qty_stock - line.packaging_qty
return res
_columns = {
'default_packaging': fields.float(string='Default packaging', readonly=True),
'packaging_qty': fields.float(string='Theorical packaging qty', readonly=True),
'qty_loss': fields.function(_get_qty_loss, type="float", string='Quantity Lost',
digits_compute=dp.get_precision('Product Unit of Measure'),
help='Quantity Theoric Of Reference - Stock Quantity'),
'qty_stock': fields.float(string='Stock Quantity', digits_compute=dp.get_precision('Product Unit of Measure'),
help="Stock Quantity"),
}
def on_change_qty_stock(self, cr, uid, ids, product_id,packaging_qty=0.0, qty_stock=0.0, default_packaging=None, context=None):
if not product_id:
return self.on_change_product_id(cr,uid,ids,product_id=product_id)
if not default_packaging:
return {'warning': {'title': _('Warning: wrong default packaging'),
'message': _('The default packaging is not defined on the product')}
}
else:
return {'value': {'qty_loss': qty_stock - packaging_qty,
'product_qty': qty_stock * default_packaging}}
def on_change_product_id(self, cr, uid, ids, product_id=None, context=None):
if not product_id:
return {'value': {'default_packaging': False,
'packaging_qty': False,
'qty_loss': False,
'product_qty': 0.0,
}}
product = self.pool.get('product.product').browse(cr, uid, product_id)
default_packaging = product.default_packaging
if default_packaging:
packaging_qty = product.qty_available / default_packaging
ret = {'value': {'default_packaging': default_packaging,
'packaging_qty': packaging_qty,
'qty_stock': 0.0,
'qty_loss': -packaging_qty,
'product_qty': 0.0
}}
else:
ret = {'value': {'default_packaging': 0.0,
'packaging_qty': 0.0,
'qty_stock': 0.0,
'qty_loss': 0.0,
'product_qty': 0.0
}}
ret['warningn'] = {'title': _('Warning: wrong default packaging'),
'message': _('The default packaging is not defined on the product')}
return ret