510
515
result[obj.id]['np_check'] = True
519
def _check_tracking(self, cr, uid, ids, context=None):
520
""" Checks if production lot is assigned to stock move or not.
521
@return: True or False
523
for move in self.browse(cr, uid, ids, context=context):
524
if not move.prodlot_id and move.product_qty and \
525
(move.state == 'done' and \
527
(move.product_id.track_production and move.location_id.usage == 'production') or \
528
(move.product_id.track_production and move.location_dest_id.usage == 'production') or \
529
(move.product_id.track_incoming and move.location_id.usage == 'supplier') or \
530
(move.product_id.track_outgoing and move.location_dest_id.usage == 'customer') \
514
535
_columns = {'kc_dg': fields.function(_kc_dg, method=True, string='KC/DG', type='char'),
515
536
# if prodlot needs to be mandatory, add 'required': ['|', ('hidden_batch_management_mandatory','=',True), ('hidden_perishable_mandatory','=',True)] in attrs
704
728
result[id] = False
731
def _stock_search_virtual(self, cr, uid, obj, name, args, context=None):
732
""" Searches Ids of products
733
@return: Ids of locations
737
# when the location_id = False results now in showing stock for all internal locations
738
# *previously*, was showing the location of no location (= 0.0 for all prodlot)
739
if 'location_id' not in context or not context['location_id']:
740
locations = self.pool.get('stock.location').search(cr, uid, [('usage', '=', 'internal')], context=context)
742
locations = context['location_id'] and [context['location_id']] or []
744
ids = [('id', 'in', [])]
750
stock_report_prodlots_virtual
752
location_id IN %s group by prodlot_id
753
having sum(qty) '''+ str(args[0][1]) + str(args[0][2]),(tuple(locations),))
755
ids = [('id', 'in', map(lambda x: x[0], res))]
707
def _get_stock(self, cr, uid, ids, field_name, arg, context=None):
758
def _stock_search(self, cr, uid, obj, name, args, context=None):
760
call super method, as fields.function does not work with inheritance
762
return super(stock_production_lot, self)._stock_search(cr, uid, obj, name, args, context=context)
764
def _get_stock_virtual(self, cr, uid, ids, field_name, arg, context=None):
708
765
""" Gets stock of products for locations
709
766
@return: Dictionary of values
711
768
if context is None:
770
# when the location_id = False results now in showing stock for all internal locations
771
# *previously*, was showing the location of no location (= 0.0 for all prodlot)
772
if 'location_id' not in context or not context['location_id']:
773
locations = self.pool.get('stock.location').search(cr, uid, [('usage', '=', 'internal')], context=context)
775
locations = context['location_id'] and [context['location_id']] or []
714
777
if isinstance(ids, (int, long)):
717
product_obj = self.pool.get('product.product')
723
for lot in self.browse(cr, uid, ids, context=context):
724
# because the lot_id changes we have to loop one lot id at a time
726
# if you remove the coma after done, it will no longer work properly
727
c.update({'what': ('in', 'out'),
728
'prodlot_id': lot.id,
729
#'to_date': time.strftime('%Y-%m-%d %H:%M:%S'),
730
#'warehouse': warehouse_id,
731
#'uom': product_uom_id
734
if field_name == 'stock_available':
736
c.update(states=('confirmed','waiting','assigned','done'))
737
elif field_name == 'stock_real':
739
c.update(states=('done',))
741
assert False, 'This line should not be reached: field_name: %s'%field_name
743
qty = product_obj.get_product_available(cr, uid, [lot.product_id.id], context=c)
744
overall_qty = sum(qty.values())
745
result[lot.id] = overall_qty
780
res = {}.fromkeys(ids, 0.0)
786
stock_report_prodlots_virtual
788
location_id IN %s and prodlot_id IN %s group by prodlot_id''',(tuple(locations),tuple(ids),))
789
res.update(dict(cr.fetchall()))
793
def _get_stock(self, cr, uid, ids, field_name, arg, context=None):
795
call super method, as fields.function does not work with inheritance
797
return super(stock_production_lot, self)._get_stock(cr, uid, ids, field_name, arg, context=context)
749
799
def _get_checks_all(self, cr, uid, ids, name, arg, context=None):
778
828
'name': fields.char('Batch Number', size=1024, required=True, help="Unique production lot, will be displayed as: PREFIX/SERIAL [INT_REF]"),
779
829
'date': fields.datetime('Auto Creation Date', required=True),
780
830
'sequence_id': fields.many2one('ir.sequence', 'Lot Sequence', required=True,),
781
'stock_available': fields.function(_get_stock, method=True, type="float", string="Available", select=True,
782
help="Current quantity of products with this Production Lot Number available in company warehouses",
783
digits_compute=dp.get_precision('Product UoM'), readonly=True,),
784
'stock_real': fields.function(_get_stock, method=True, type="float", string="Real", select=True,
785
help="Current quantity of products with this Production Lot Number available in company warehouses",
786
digits_compute=dp.get_precision('Product UoM'), readonly=True,),
831
'stock_virtual': fields.function(_get_stock_virtual, method=True, type="float", string="Available Stock", select=True,
832
help="Current available quantity of products with this Production Lot Number in company warehouses",
833
digits_compute=dp.get_precision('Product UoM'), readonly=True,
834
fnct_search=_stock_search_virtual,),
835
'stock_available': fields.function(_get_stock, fnct_search=_stock_search, method=True, type="float", string="Real Stock", select=True,
836
help="Current real quantity of products with this Production Lot Number in company warehouses",
837
digits_compute=dp.get_precision('Product UoM')),
787
838
'kc_check': fields.function(_get_checks_all, method=True, string='KC', type='boolean', readonly=True, multi="m"),
788
839
'ssl_check': fields.function(_get_checks_all, method=True, string='SSL', type='boolean', readonly=True, multi="m"),
789
840
'dg_check': fields.function(_get_checks_all, method=True, string='DG', type='boolean', readonly=True, multi="m"),
820
871
stock_production_lot()
874
class stock_location(osv.osv):
876
override stock location to add:
880
_inherit = 'stock.location'
882
def replace_field_key(self, fieldsDic, search, replace):
884
will replace 'stock_real' by 'stock_real_specific'
885
and 'stock_virtual' by 'stock_virtual_specific'
887
and return a new dictionary
889
return dict((replace if key == search else key, (self.replace_field_key(value, search, replace) if isinstance(value, dict) else value)) for key, value in fieldsDic.items())
891
def _product_value_specific_rules(self, cr, uid, ids, field_names, arg, context=None):
893
add two fields for custom stock computation, if no product selected, both stock are set to 0.0
901
for f in field_names:
902
result[id].update({f: False,})
903
# if product is set to False, it does not make sense to return a stock value, return False for each location
904
if 'product_id' in context and not context['product_id']:
907
result = super(stock_location, self)._product_value(cr, uid, ids, ['stock_real', 'stock_virtual'], arg, context=context)
909
result = self.replace_field_key(result, 'stock_real', 'stock_real_specific')
910
# replace stock virtual
911
result = self.replace_field_key(result, 'stock_virtual', 'stock_virtual_specific')
914
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
916
display the modified stock values (stock_real_specific, stock_virtual_specific) if needed
920
# warehouse wizards or inventory screen
921
if view_type == 'tree' and context.get('specific_rules_tree_view', False):
922
view = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'specific_rules', 'view_location_tree2')
925
result = super(osv.osv, self).fields_view_get(cr, uid, view_id, view_type, context=context, toolbar=toolbar, submenu=submenu)
928
_columns = {'stock_real_specific': fields.function(_product_value_specific_rules, method=True, type='float', string='Real Stock', multi="get_vals_specific_rules"),
929
'stock_virtual_specific': fields.function(_product_value_specific_rules, method=True, type='float', string='Virtual Stock', multi="get_vals_specific_rules"),
823
935
class stock_production_lot_revision(osv.osv):
824
936
_inherit = 'stock.production.lot.revision'
825
937
_order = 'indice desc'
879
991
_inherit = 'stock.inventory.line'
881
def change_lot(self, cr, uid, id, prod_lot_id, context=None):
993
def common_on_change(self, cr, uid, ids, location_id, product, prod_lot_id, uom=False, to_date=False, result=None):
995
commmon qty computation
1001
product_obj = self.pool.get('product.product').browse(cr, uid, product)
1002
uom = uom or product_obj.uom_id.id
1003
stock_context = {'uom': uom, 'to_date': to_date,
1004
'prodlot_id':prod_lot_id,}
1006
# if a location is specified, we do not list the children locations, otherwise yes
1007
stock_context.update({'compute_child': False,})
1008
amount = self.pool.get('stock.location')._product_get(cr, uid, location_id, [product], stock_context)[product]
1009
result.setdefault('value', {}).update({'product_qty': amount, 'product_uom': uom})
1012
def change_lot(self, cr, uid, ids, location_id, product, prod_lot_id, uom=False, to_date=False,):
883
1014
prod lot changes, update the expiry date
885
1016
prodlot_obj = self.pool.get('stock.production.lot')
886
1017
result = {'value':{}}
1018
# reset expiry date or fill it
889
result['value'].update(expiry_date=prodlot_obj.browse(cr, uid, prod_lot_id, context).life_date)
1020
result['value'].update(expiry_date=prodlot_obj.browse(cr, uid, prod_lot_id).life_date)
891
1022
result['value'].update(expiry_date=False)
1024
result = self.common_on_change(cr, uid, ids, location_id, product, prod_lot_id, uom, to_date, result=result)
895
1027
def change_expiry(self, cr, uid, id, expiry_date, product_id, type_check, context=None):
920
1052
# return first prodlot
921
1053
result['value'].update(prod_lot_id=prod_ids[0])
924
1055
# clear expiry date, we clear production lot
925
1056
result['value'].update(prod_lot_id=False,
926
1057
expiry_date=False,
1061
def on_change_location_id(self, cr, uid, ids, location_id, product, prod_lot_id, uom=False, to_date=False,):
1062
""" Changes UoM and name if product_id changes.
1063
@param location_id: Location id
1064
@param product: Changed product_id
1065
@param uom: UoM product
1066
@return: Dictionary of changed values
1071
result.setdefault('value', {}).update({'product_qty': 0.0,})
1074
result = self.common_on_change(cr, uid, ids, location_id, product, prod_lot_id, uom, to_date, result=result)
1077
def on_change_product_id_specific_rules(self, cr, uid, ids, location_id, product, prod_lot_id, uom=False, to_date=False,):
1079
the product changes, set the hidden flag if necessary
1081
result = super(stock_inventory_line, self).on_change_product_id(cr, uid, ids, location_id, product, uom, to_date)
1082
# product changes, prodlot is always cleared
1083
result.setdefault('value', {})['prod_lot_id'] = False
1084
result.setdefault('value', {})['expiry_date'] = False
1085
# reset the hidden flags
1086
result.setdefault('value', {})['hidden_batch_management_mandatory'] = False
1087
result.setdefault('value', {})['hidden_perishable_mandatory'] = False
1089
product_obj = self.pool.get('product.product').browse(cr, uid, product)
1090
if product_obj.batch_management:
1091
result.setdefault('value', {})['hidden_batch_management_mandatory'] = True
1092
elif product_obj.perishable:
1093
result.setdefault('value', {})['hidden_perishable_mandatory'] = True
1094
# if not product, result is 0.0 by super
1096
result = self.common_on_change(cr, uid, ids, location_id, product, prod_lot_id, uom, to_date, result=result)
1099
def create(self, cr, uid, vals, context=None):
1101
complete info normally generated by javascript on_change function
1103
prod_obj = self.pool.get('product.product')
1104
if vals.get('product_id', False):
1105
# complete hidden flags - needed if not created from GUI
1106
product = prod_obj.browse(cr, uid, vals.get('product_id'), context=context)
1107
if product.batch_management:
1108
vals.update(hidden_batch_management_mandatory=True)
1109
elif product.perishable:
1110
vals.update(hidden_perishable_mandatory=True)
1112
vals.update(hidden_batch_management_mandatory=False,
1113
hidden_perishable_mandatory=False,
1115
# complete expiry date from production lot - needed if not created from GUI
1116
prodlot_obj = self.pool.get('stock.production.lot')
1117
if vals.get('prod_lot_id', False):
1118
vals.update(expiry_date=prodlot_obj.browse(cr, uid, vals.get('prod_lot_id'), context=context).life_date)
1120
result = super(stock_inventory_line, self).create(cr, uid, vals, context=context)
1123
def write(self, cr, uid, ids, vals, context=None):
1125
complete info normally generated by javascript on_change function
1127
prod_obj = self.pool.get('product.product')
1128
if vals.get('product_id', False):
1129
# complete hidden flags - needed if not created from GUI
1130
product = prod_obj.browse(cr, uid, vals.get('product_id'), context=context)
1131
if product.batch_management:
1132
vals.update(hidden_batch_management_mandatory=True)
1133
elif product.perishable:
1134
vals.update(hidden_perishable_mandatory=True)
1136
vals.update(hidden_batch_management_mandatory=False,
1137
hidden_perishable_mandatory=False,
1139
# complete expiry date from production lot - needed if not created from GUI
1140
prodlot_obj = self.pool.get('stock.production.lot')
1141
if vals.get('prod_lot_id', False):
1142
vals.update(expiry_date=prodlot_obj.browse(cr, uid, vals.get('prod_lot_id'), context=context).life_date)
1145
result = super(stock_inventory_line, self).write(cr, uid, ids, vals, context=context)
931
1148
def _get_checks_all(self, cr, uid, ids, name, arg, context=None):
1008
1225
'The selected product is neither Batch Number Mandatory nor Expiry Date Mandatory',
1009
1226
['prod_lot_id']),
1012
def on_change_product_id(self, cr, uid, ids, location_id, product, uom=False, to_date=False):
1014
the product changes, set the hidden flag if necessary
1016
result = super(stock_inventory_line, self).on_change_product_id(cr, uid, ids, location_id, product, uom, to_date)
1018
# product changes, prodlot is always cleared
1019
result.setdefault('value', {})['prod_lot_id'] = False
1020
result.setdefault('value', {})['expiry_date'] = False
1021
# reset the hidden flags
1022
result.setdefault('value', {})['hidden_batch_management_mandatory'] = False
1023
result.setdefault('value', {})['hidden_perishable_mandatory'] = False
1025
product_obj = self.pool.get('product.product').browse(cr, uid, product)
1026
if product_obj.batch_management:
1027
result.setdefault('value', {})['hidden_batch_management_mandatory'] = True
1028
elif product_obj.perishable:
1029
result.setdefault('value', {})['hidden_perishable_mandatory'] = True
1033
def create(self, cr, uid, vals, context=None):
1035
complete info normally generated by javascript on_change function
1037
prod_obj = self.pool.get('product.product')
1038
if vals.get('product_id', False):
1039
# complete hidden flags - needed if not created from GUI
1040
product = prod_obj.browse(cr, uid, vals.get('product_id'), context=context)
1041
if product.batch_management:
1042
vals.update(hidden_batch_management_mandatory=True)
1043
elif product.perishable:
1044
vals.update(hidden_perishable_mandatory=True)
1046
vals.update(hidden_batch_management_mandatory=False,
1047
hidden_perishable_mandatory=False,
1049
# complete expiry date from production lot - needed if not created from GUI
1050
prodlot_obj = self.pool.get('stock.production.lot')
1051
if vals.get('prod_lot_id', False):
1052
vals.update(expiry_date=prodlot_obj.browse(cr, uid, vals.get('prod_lot_id'), context=context).life_date)
1054
result = super(stock_inventory_line, self).create(cr, uid, vals, context=context)
1057
def write(self, cr, uid, ids, vals, context=None):
1059
complete info normally generated by javascript on_change function
1061
prod_obj = self.pool.get('product.product')
1062
if vals.get('product_id', False):
1063
# complete hidden flags - needed if not created from GUI
1064
product = prod_obj.browse(cr, uid, vals.get('product_id'), context=context)
1065
if product.batch_management:
1066
vals.update(hidden_batch_management_mandatory=True)
1067
elif product.perishable:
1068
vals.update(hidden_perishable_mandatory=True)
1070
vals.update(hidden_batch_management_mandatory=False,
1071
hidden_perishable_mandatory=False,
1073
# complete expiry date from production lot - needed if not created from GUI
1074
prodlot_obj = self.pool.get('stock.production.lot')
1075
if vals.get('prod_lot_id', False):
1076
vals.update(expiry_date=prodlot_obj.browse(cr, uid, vals.get('prod_lot_id'), context=context).life_date)
1079
result = super(stock_inventory_line, self).write(cr, uid, ids, vals, context=context)
1082
1229
stock_inventory_line()