~openerp-community/openobject-addons/trunk-addons-community

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
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
# -*- encoding: utf-8 -*-
##############################################################################
#
#    Product loan module for OpenERP
#    Copyright (C) 2009-2011 Anevia S.A. (www.anevia.com),
#       code written by Raphaêl Valyi from Smile.fr,
#           now working at Akretion.com <rvalyi@akretion.com>
#
#    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/>.
#
##############################################################################


from openerp.osv import fields, orm
from openerp import netsvc
import time
from datetime import datetime
from openerp.tools.translate import _


class sale_order_line(orm.Model):
    _inherit = "sale.order.line"

    _columns = {
        'prodlot_id': fields.many2one('stock.production.lot', 'Serial number'),
        'is_loan': fields.related('order_id', 'is_loan', type='boolean', string='Is Loan?'),
        # track_loan is NOT a fields.related the track_loan field of
        # product.template because, if you do that, then OpenERP tries to write
        # on product.template when you update a sale order line !
        'track_loan': fields.boolean('Track on loan'),
        'product_type': fields.char('Product type', size=32),
    }

    def product_id_change(self, cr, uid, ids, pricelist, product_id, qty=0,
            uom=False, qty_uos=0, uos=False, name='', partner_id=False,
            lang=False, update_tax=True, date_order=False, packaging=False,
            fiscal_position=False, flag=False, is_maintenance=False,
            maintenance_product_qty=False, maintenance_month_qty=False,
            default_fleet_id=False, subfleet_required=False, context=None):

        if context is None:
            context = {}

        result = super(sale_order_line, self).product_id_change(cr, uid, ids, pricelist, product_id, qty, uom, qty_uos, uos, name, partner_id, lang, update_tax, date_order, packaging, fiscal_position, flag, is_maintenance, maintenance_product_qty, maintenance_month_qty, default_fleet_id, subfleet_required, context=context)

        if product_id:
            read_product = self.pool.get('product.product').read(cr, uid, product_id, ['track_loan', 'type'], context=context)
            result['value']['track_loan'] = read_product['track_loan']
            result['value']['product_type'] = read_product['type']

        if context.get('is_loan'):
            result['value']['is_loan'] = True
        return result

    def _check_single_product(self, cr, uid, ids):
        for order_line in self.browse(cr, uid, ids):
            if order_line.is_loan and order_line.product_id and order_line.product_id.track_loan and order_line.product_uom_qty != 1:
                raise orm.except_orm(
                    _('Error:'),
                    _("The loan line with description '%s' must have a quantity of 1 because the product has the 'Track Loan' option set.") % order_line.name)
        return True

    _constraints = [
        (_check_single_product,
            """Error msg in raise""", ['is_loan', 'product_uom_qty', 'product_id'])]


class sale_order(orm.Model):
    _inherit = "sale.order"

    def _default_name(self, cr, uid, context=None):
        if context is None:
            context = {}
        if context.get('is_loan', False):
            return self.pool.get('ir.sequence').get(cr, uid, 'loan')
        else:
            return self.pool.get('ir.sequence').get(cr, uid, 'sale.order')


    def _is_loan_expired(self, cr, uid, ids, field_name, arg, context=None):
        result = {}
        for order in self.browse(cr, uid, ids, context=context):
            result[order.id] = False
            if order.is_loan and order.loan_end_date < time.strftime('%Y-%m-%d'):
                for picking in order.picking_ids:
                    if (picking.type == 'in') and (picking.state in ('draft','assigned','confirmed','auto')):
                        result[order.id] = True
        return result

    def _is_loan_expired_search(self, cr, uid, obj, name, args, context=None):
        '''I need to defined this fnct_search because I want to be able to
        filter on the field 'is_loan_expired' in the search view for loans
        More info https://bugs.launchpad.net/openobject-addons/+bug/850752'''
        loans_ids = self.search(cr, uid, [('is_loan', '=', True)], context=context)
        res = self._is_loan_expired(cr, uid, loans_ids, None, None, context=context)
        result = [res_id for res_id, is_expired in res.iteritems() if is_expired == args[0][2]]
        return [('id', 'in', result)]

    _defaults = {
        'name': _default_name,
        'is_loan': lambda self, cr, uid, context: context.get('is_loan', False),
    }

    _columns = {
        'is_loan': fields.boolean('Is Loan?'),
        'is_loan_expired': fields.function(_is_loan_expired, type='boolean', string='Is Expired?', fnct_search=_is_loan_expired_search),
        'loan_start_date': fields.date('Loan Start Date', required=False),
        'loan_end_date': fields.date('Loan End Date', required=False),
    }

    # Set the name with the loan sequence instead of the
    # sale order sequence
    def copy(self, cr, uid, id, default=None, context=None):
        order_id = super(sale_order, self).copy(
            cr, uid, id, default=default, context=context)
        if context.get('is_loan'):
            self.pool['sale.order'].write(cr, uid, order_id, {
                'name': self.pool['ir.sequence'].get(cr, uid, 'loan')
            }, context=context)

        return order_id


    # Modify the search method for taking the is_loan_expired search criteria
    # If true, only the expired loan are returned
    def search(self, cr, uid, args, offset=0, limit=None, order=None,
             context=None, count=False):

        ids = super(sale_order, self).search(cr, uid, args, offset, limit,
                order, context=context, count=count)
        new_ids = []

        if context and context.has_key('loan_search') and context['loan_search'] is True:
            pos = 0
            is_loan_expired = False

            while pos < len(args):
                if args[pos][0] == 'is_loan_expired':
                    if args[pos][2] == "True":
                        is_loan_expired = True
                pos += 1

            if is_loan_expired:
                for loan_id in ids:
                    loan = self.pool.get('sale.order').browse(cr, uid, loan_id)
                    if loan.is_loan_expired:
                        new_ids.append(loan_id)
            else:
                new_ids = ids
        else:
                new_ids = ids

        return new_ids

    def _prepare_order_picking(self, cr, uid, order, context=None):
        picking_vals = super(sale_order, self)._prepare_order_picking(cr, uid, order, context=context)
        if order.is_loan:
            picking_vals.update({
                'min_date': order.loan_start_date,
                'invoice_state': 'none',
            })
        return picking_vals

    def _prepare_order_line_move(
            self, cr, uid, order, line, picking_id, date_planned,
            context=None):
        if context is None:
            context = {}
        move_vals = super(sale_order, self)._prepare_order_line_move(
            cr, uid, order, line, picking_id, date_planned, context=context)
        if order.is_loan:
            if context.get('loan_in'):
                location_id = order.shop_id.warehouse_id.loan_out_location_id.id
                location_dest_id = order.shop_id.warehouse_id.loan_in_location_id.id
                date = order.loan_end_date
                move_dest_id = False
            else:
                location_id = order.shop_id.warehouse_id.loan_in_location_id.id
                location_dest_id = order.shop_id.warehouse_id.loan_out_location_id.id
                date = order.loan_start_date
                line_in_move = context.get('line_in_move')
                if line_in_move:
                    move_dest_id = line_in_move.get(line.id, False)
            move_vals.update({
                'date': date,
                'date_expected': date,
                'location_id': location_id,
                'location_dest_id': location_dest_id,
                'prodlot_id': line.prodlot_id.id,
                'move_dest_id': move_dest_id,
                })
        return move_vals

    def _prepare_loan_picking_in(self, cr, uid, order, context=None):
        return {
            'name': self.pool['ir.sequence'].get(cr, uid, 'stock.picking.in'),
            'origin': order.name,
            'date': order.loan_end_date,
            'type': 'in',
            'state': 'auto',
            'move_type': order.picking_policy,
            'sale_id': order.id,
            'address_id': order.partner_shipping_id.id,
            'note': order.note,
            'invoice_state': 'none',
            'company_id': order.company_id.id,
        }

    def _create_pickings_and_procurements(self, cr, uid, order, order_lines, picking_id=False, context=None):
        if context is None:
            context = {}
        line_in_move = {}  # key = line ID, value = in_move_id
        if order.is_loan:
            # Create an incoming picking is we have at least one "stockable product"
            if any([order_line.product_id.type == 'product' for order_line in order_lines]):
                picking_obj = self.pool['stock.picking']
                move_obj = self.pool['stock.move']

                return_picking_id = picking_obj.create(
                    cr, uid, self._prepare_loan_picking_in(
                        cr, uid, order, context=context),
                    context=context)

                move_in_ctx = context.copy()
                move_in_ctx['loan_in'] = True

                for line in order_lines:
                    if line.product_id.type == 'product':
                        # consu should not be return
                        in_move_id = move_obj.create(
                            cr, uid, self._prepare_order_line_move(
                                cr, uid, order, line, return_picking_id, False,
                                context=move_in_ctx),
                            context=context)
                        line_in_move[line.id] = in_move_id

                wf_service = netsvc.LocalService("workflow")
                wf_service.trg_validate(
                    uid, 'stock.picking', return_picking_id, 'button_confirm', cr)

        out_ctx = context.copy()
        out_ctx['line_in_move'] = line_in_move
        res = super(sale_order, self)._create_pickings_and_procurements(
            cr, uid, order, order_lines, picking_id, context=out_ctx)
        return res

    def _check_loan_date_order(self, cr, uid, ids):
        for order in self.browse(cr, uid, ids):
            if order.is_loan:
                date_start = datetime.strptime(order.loan_start_date, '%Y-%m-%d')
                date_end = datetime.strptime(order.loan_end_date, '%Y-%m-%d')
                if (date_start > date_end):
                    return False
        return True

    _constraints = [
        (_check_loan_date_order,
            """Start date should be before end date and should not be in the past !""", ['is_loan', 'loan_start_date', 'loan_end_date']),
            ]