~unifield-team/unifield-wm/us-826

« back to all changes in this revision

Viewing changes to specific_rules/specific_rules.py

  • Committer: Olivier DOSSMANN
  • Date: 2011-12-28 15:38:17 UTC
  • mto: This revision was merged to the branch mainline in revision 492.
  • Revision ID: olivier@tempo-laptop-20111228153817-rd119phuevo66e3y
UF-675 [FIX] Engagement lines deletion when a PO is cancelled.

Show diffs side-by-side

added added

removed removed

Lines of Context:
413
413
        for move in self.browse(cr, uid, ids, context=context):
414
414
            if move.state == 'done':
415
415
                if move.product_id.batch_management:
416
 
                    if not move.prodlot_id:
 
416
                    if not move.prodlot_id and move.product_qty:
417
417
                        return False
418
418
        return True
419
419
    
425
425
        for move in self.browse(cr, uid, ids, context=context):
426
426
            if move.state == 'done':
427
427
                if move.product_id.perishable:
428
 
                    if not move.prodlot_id:
 
428
                    if not move.prodlot_id and move.product_qty:
429
429
                        return False
430
430
        return True
431
431
    
482
482
                result.setdefault('value', {})['hidden_perishable_mandatory'] = True
483
483
                result['warning'] = {'title': _('Info'),
484
484
                                     'message': _('The selected product is Perishable.')}
 
485
                
 
486
        # quantities are set to False
 
487
        result.setdefault('value', {}).update({'product_qty': 0.00,
 
488
                                               'product_uos_qty': 0.00,
 
489
                                               })
485
490
            
486
491
        return result
487
492
    
510
515
                result[obj.id]['np_check'] = True
511
516
            
512
517
        return result
 
518
    
 
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
 
522
        """
 
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 \
 
526
               ( \
 
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') \
 
531
               )):
 
532
                return False
 
533
        return True
513
534
            
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
536
557
                    (_check_prodlot_need_perishable,
537
558
                     'The selected product is Expiry Date Mandatory while the selected Production Lot corresponds to Batch Number Mandatory.',
538
559
                     ['prodlot_id']),
 
560
                     (_check_tracking,
 
561
                      'You must assign a production lot for this product',
 
562
                      ['prodlot_id']),
539
563
                    ]
540
564
 
541
565
stock_move()
703
727
        for id in ids:
704
728
          result[id] = False
705
729
        return result
 
730
 
 
731
    def _stock_search_virtual(self, cr, uid, obj, name, args, context=None):
 
732
        """ Searches Ids of products
 
733
        @return: Ids of locations
 
734
        """
 
735
        if context is None:
 
736
            context = {}
 
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)
 
741
        else:
 
742
            locations = context['location_id'] and [context['location_id']] or []
 
743
        
 
744
        ids = [('id', 'in', [])]
 
745
        if locations:
 
746
            cr.execute('''select
 
747
                    prodlot_id,
 
748
                    sum(qty)
 
749
                from
 
750
                    stock_report_prodlots_virtual
 
751
                where
 
752
                    location_id IN %s group by prodlot_id
 
753
                having  sum(qty) '''+ str(args[0][1]) + str(args[0][2]),(tuple(locations),))
 
754
            res = cr.fetchall()
 
755
            ids = [('id', 'in', map(lambda x: x[0], res))]
 
756
        return ids
706
757
    
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):
 
759
        '''
 
760
        call super method, as fields.function does not work with inheritance
 
761
        '''
 
762
        return super(stock_production_lot, self)._stock_search(cr, uid, obj, name, args, context=context)
 
763
 
 
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
710
767
        """
711
768
        if context is None:
712
769
            context = {}
713
 
            
 
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)
 
774
        else:
 
775
            locations = context['location_id'] and [context['location_id']] or []
 
776
 
714
777
        if isinstance(ids, (int, long)):
715
778
            ids = [ids]
716
 
            
717
 
        product_obj = self.pool.get('product.product')
718
 
        
719
 
        result = {}
720
 
        for id in ids:
721
 
            result[id] = 0.0
722
 
        
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
725
 
            c = context.copy()
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
732
 
                      })
733
 
            
734
 
            if field_name == 'stock_available':
735
 
                # available stock
736
 
                c.update(states=('confirmed','waiting','assigned','done'))
737
 
            elif field_name == 'stock_real':
738
 
                # real stock
739
 
                c.update(states=('done',))
740
 
            else:
741
 
                assert False, 'This line should not be reached: field_name: %s'%field_name
742
 
            
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
746
 
        
747
 
        return result
 
779
 
 
780
        res = {}.fromkeys(ids, 0.0)
 
781
        if locations:
 
782
            cr.execute('''select
 
783
                    prodlot_id,
 
784
                    sum(qty)
 
785
                from
 
786
                    stock_report_prodlots_virtual
 
787
                where
 
788
                    location_id IN %s and prodlot_id IN %s group by prodlot_id''',(tuple(locations),tuple(ids),))
 
789
            res.update(dict(cr.fetchall()))
 
790
 
 
791
        return res
 
792
    
 
793
    def _get_stock(self, cr, uid, ids, field_name, arg, context=None):
 
794
        '''
 
795
        call super method, as fields.function does not work with inheritance
 
796
        '''
 
797
        return super(stock_production_lot, self)._get_stock(cr, uid, ids, field_name, arg, context=context)
748
798
    
749
799
    def _get_checks_all(self, cr, uid, ids, name, arg, context=None):
750
800
        '''
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()
821
872
 
822
873
 
 
874
class stock_location(osv.osv):
 
875
    '''
 
876
    override stock location to add:
 
877
    - stock_real
 
878
    - stock_virtual
 
879
    '''
 
880
    _inherit = 'stock.location'
 
881
    
 
882
    def replace_field_key(self, fieldsDic, search, replace):
 
883
        '''
 
884
        will replace 'stock_real' by 'stock_real_specific'
 
885
        and 'stock_virtual' by 'stock_virtual_specific'
 
886
        
 
887
        and return a new dictionary
 
888
        '''
 
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())
 
890
    
 
891
    def _product_value_specific_rules(self, cr, uid, ids, field_names, arg, context=None):
 
892
        '''
 
893
        add two fields for custom stock computation, if no product selected, both stock are set to 0.0
 
894
        '''
 
895
        if context is None:
 
896
            context = {}
 
897
        # initialize data
 
898
        result = {}
 
899
        for id in ids:
 
900
            result[id] = {}
 
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']:
 
905
            return result
 
906
        
 
907
        result = super(stock_location, self)._product_value(cr, uid, ids, ['stock_real', 'stock_virtual'], arg, context=context)
 
908
        # replace stock real
 
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')
 
912
        return result
 
913
    
 
914
    def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
 
915
        """
 
916
        display the modified stock values (stock_real_specific, stock_virtual_specific) if needed
 
917
        """
 
918
        if context is None:
 
919
            context = {}
 
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')
 
923
            if view:
 
924
                view_id = view[1]
 
925
        result = super(osv.osv, self).fields_view_get(cr, uid, view_id, view_type, context=context, toolbar=toolbar, submenu=submenu)
 
926
        return result
 
927
    
 
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"),
 
930
                }
 
931
    
 
932
stock_location()
 
933
 
 
934
 
823
935
class stock_production_lot_revision(osv.osv):
824
936
    _inherit = 'stock.production.lot.revision'
825
937
    _order = 'indice desc'
878
990
    '''
879
991
    _inherit = 'stock.inventory.line'
880
992
    
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):
 
994
        '''
 
995
        commmon qty computation
 
996
        '''
 
997
        if result is None:
 
998
            result = {}
 
999
        if not product:
 
1000
            return result
 
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,}
 
1005
        if location_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})
 
1010
        return result
 
1011
    
 
1012
    def change_lot(self, cr, uid, ids, location_id, product, prod_lot_id, uom=False, to_date=False,):
882
1013
        '''
883
1014
        prod lot changes, update the expiry date
884
1015
        '''
885
1016
        prodlot_obj = self.pool.get('stock.production.lot')
886
1017
        result = {'value':{}}
887
 
        
 
1018
        # reset expiry date or fill it
888
1019
        if prod_lot_id:
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)
890
1021
        else:
891
1022
            result['value'].update(expiry_date=False)
892
 
        
 
1023
        # compute qty
 
1024
        result = self.common_on_change(cr, uid, ids, location_id, product, prod_lot_id, uom, to_date, result=result)
893
1025
        return result
894
1026
    
895
1027
    def change_expiry(self, cr, uid, id, expiry_date, product_id, type_check, context=None):
919
1051
            else:
920
1052
                # return first prodlot
921
1053
                result['value'].update(prod_lot_id=prod_ids[0])
922
 
                
923
1054
        else:
924
1055
            # clear expiry date, we clear production lot
925
1056
            result['value'].update(prod_lot_id=False,
926
1057
                                   expiry_date=False,
927
1058
                                   )
 
1059
        return result
 
1060
    
 
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
 
1067
        """
 
1068
        result = {}
 
1069
        if not product:
 
1070
            # do nothing
 
1071
            result.setdefault('value', {}).update({'product_qty': 0.0,})
 
1072
            return result
 
1073
        # compute qty
 
1074
        result = self.common_on_change(cr, uid, ids, location_id, product, prod_lot_id, uom, to_date, result=result)
 
1075
        return result
 
1076
    
 
1077
    def on_change_product_id_specific_rules(self, cr, uid, ids, location_id, product, prod_lot_id, uom=False, to_date=False,):
 
1078
        '''
 
1079
        the product changes, set the hidden flag if necessary
 
1080
        '''
 
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
 
1088
        if product:
 
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
 
1095
            # compute qty
 
1096
            result = self.common_on_change(cr, uid, ids, location_id, product, prod_lot_id, uom, to_date, result=result)
 
1097
        return result
 
1098
    
 
1099
    def create(self, cr, uid, vals, context=None):
 
1100
        '''
 
1101
        complete info normally generated by javascript on_change function
 
1102
        '''
 
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)
 
1111
            else:
 
1112
                vals.update(hidden_batch_management_mandatory=False,
 
1113
                            hidden_perishable_mandatory=False,
 
1114
                            )
 
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)
 
1119
        # call super
 
1120
        result = super(stock_inventory_line, self).create(cr, uid, vals, context=context)
 
1121
        return result
 
1122
    
 
1123
    def write(self, cr, uid, ids, vals, context=None):
 
1124
        '''
 
1125
        complete info normally generated by javascript on_change function
 
1126
        '''
 
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)
 
1135
            else:
 
1136
                vals.update(hidden_batch_management_mandatory=False,
 
1137
                            hidden_perishable_mandatory=False,
 
1138
                            )
 
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)
928
1143
        
 
1144
        # call super
 
1145
        result = super(stock_inventory_line, self).write(cr, uid, ids, vals, context=context)
929
1146
        return result
930
1147
    
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']),
1010
1227
                    ]
1011
 
    
1012
 
    def on_change_product_id(self, cr, uid, ids, location_id, product, uom=False, to_date=False):
1013
 
        '''
1014
 
        the product changes, set the hidden flag if necessary
1015
 
        '''
1016
 
        result = super(stock_inventory_line, self).on_change_product_id(cr, uid, ids, location_id, product, uom, to_date)
1017
 
        
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
1024
 
        if product:
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
1030
 
            
1031
 
        return result
1032
 
    
1033
 
    def create(self, cr, uid, vals, context=None):
1034
 
        '''
1035
 
        complete info normally generated by javascript on_change function
1036
 
        '''
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)
1045
 
            else:
1046
 
                vals.update(hidden_batch_management_mandatory=False,
1047
 
                            hidden_perishable_mandatory=False,
1048
 
                            )
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)
1053
 
        # call super
1054
 
        result = super(stock_inventory_line, self).create(cr, uid, vals, context=context)
1055
 
        return result
1056
 
    
1057
 
    def write(self, cr, uid, ids, vals, context=None):
1058
 
        '''
1059
 
        complete info normally generated by javascript on_change function
1060
 
        '''
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)
1069
 
            else:
1070
 
                vals.update(hidden_batch_management_mandatory=False,
1071
 
                            hidden_perishable_mandatory=False,
1072
 
                            )
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)
1077
 
        
1078
 
        # call super
1079
 
        result = super(stock_inventory_line, self).write(cr, uid, ids, vals, context=context)
1080
 
        return result
1081
1228
 
1082
1229
stock_inventory_line()
1083
1230