20
20
##############################################################################
22
22
from osv import osv, fields
23
from order_types import ORDER_PRIORITY, ORDER_CATEGORY
23
from tools.translate import _
25
25
from datetime import datetime, timedelta
26
26
from dateutil.relativedelta import relativedelta
27
from mx.DateTime import *
28
from tools.translate import _
31
28
class sale_order(osv.osv):
29
_inherit = 'sale.order'
32
30
_name = 'sale.order'
33
_inherit = 'sale.order'
35
def copy(self, cr, uid, id, default, context={}):
37
Delete the loan_id field on the new sale.order
39
return super(sale_order, self).copy(cr, uid, id, default={'loan_id': False}, context=context)
41
#@@@override sale.sale_order._invoiced
42
def _invoiced(self, cr, uid, ids, name, arg, context={}):
44
Return True is the sale order is an uninvoiced order
46
partner_obj = self.pool.get('res.partner')
50
for sale in self.browse(cr, uid, ids):
52
partner = partner_obj.browse(cr, uid, [sale.partner_id.id])[0]
53
if sale.state != 'draft' and (sale.order_type != 'regular' or (partner and partner.partner_type == 'internal')):
57
for invoice in sale.invoice_ids:
58
if invoice.state != 'paid':
61
if not sale.invoice_ids:
66
#@@@override sale.sale_order._invoiced_search
67
def _invoiced_search(self, cursor, user, obj, name, args, context={}):
76
clause += 'AND inv.state = \'paid\' OR (sale.state != \'draft\' AND (sale.order_type != \'regular\' OR part.partner_type = \'internal\'))'
78
clause += 'AND inv.state != \'cancel\' AND sale.state != \'cancel\' AND inv.state <> \'paid\' AND sale.order_type = \'regular\''
81
cursor.execute('SELECT rel.order_id ' \
82
'FROM sale_order_invoice_rel AS rel, account_invoice AS inv, sale_order AS sale, res_partner AS part '+ sale_clause + \
83
'WHERE rel.invoice_id = inv.id AND rel.order_id = sale.id AND sale.partner_id = part.id ' + clause)
84
res = cursor.fetchall()
86
cursor.execute('SELECT sale.id ' \
87
'FROM sale_order AS sale, res_partner AS part ' \
88
'WHERE sale.id NOT IN ' \
89
'(SELECT rel.order_id ' \
90
'FROM sale_order_invoice_rel AS rel) and sale.state != \'cancel\'' \
91
'AND sale.partner_id = part.id ' \
92
'AND sale.order_type = \'regular\' AND part.partner_type != \'internal\'')
93
res.extend(cursor.fetchall())
95
return [('id', '=', 0)]
96
return [('id', 'in', [x[0] for x in res])]
99
#@@@override sale.sale_order._invoiced_rate
100
def _invoiced_rate(self, cursor, user, ids, name, arg, context=None):
102
for sale in self.browse(cursor, user, ids, context=context):
107
for invoice in sale.invoice_ids:
108
if invoice.state not in ('draft', 'cancel'):
109
tot += invoice.amount_untaxed
111
res[sale.id] = min(100.0, tot * 100.0 / (sale.amount_untaxed or 1.00))
117
def _get_noinvoice(self, cr, uid, ids, name, arg, context={}):
119
for sale in self.browse(cr, uid, ids):
120
res[sale.id] = sale.order_type != 'regular' or sale.partner_id.partner_type == 'internal'
124
'order_type': fields.selection([('regular', 'Regular'), ('donation_exp', 'Donation before expiry'),
125
('donation_st', 'Standard donation (for help)'), ('loan', 'Loan'),],
126
string='Order Type', required=True, readonly=True, states={'draft': [('readonly', False)]}),
127
'loan_id': fields.many2one('purchase.order', string='Linked loan', readonly=True),
128
'priority': fields.selection(ORDER_PRIORITY, string='Priority', readonly=True, states={'draft': [('readonly', False)]}),
129
'categ': fields.selection(ORDER_CATEGORY, string='Order category', required=True, readonly=True, states={'draft': [('readonly', False)]}),
130
'details': fields.char(size=30, string='Details', readonly=True, states={'draft': [('readonly', False)]}),
131
'invoiced': fields.function(_invoiced, method=True, string='Paid',
132
fnct_search=_invoiced_search, type='boolean', help="It indicates that an invoice has been paid."),
133
'invoiced_rate': fields.function(_invoiced_rate, method=True, string='Invoiced', type='float'),
134
'noinvoice': fields.function(_get_noinvoice, method=True, string="Don't create an invoice", type='boolean'),
135
'loan_duration': fields.integer(string='Loan duration', help='Loan duration in months', readonly=True, states={'draft': [('readonly', False)]}),
136
'from_yml_test': fields.boolean('Only used to pass addons unit test', readonly=True, help='Never set this field to true !'),
140
'order_type': lambda *a: 'regular',
141
'priority': lambda *a: 'normal',
142
'categ': lambda *a: 'mixed',
143
'loan_duration': lambda *a: 2,
144
'from_yml_test': lambda *a: False,
147
def create(self, cr, uid, vals, context={}):
150
if context.get('update_mode') in ['init', 'update']:
151
logging.getLogger('init').info('SO: set from yml test to True')
152
vals['from_yml_test'] = True
153
return super(sale_order, self).create(cr, uid, vals, context)
155
def action_wait(self, cr, uid, ids, *args):
157
Checks if the invoice should be create from the sale order
160
line_obj = self.pool.get('sale.order.line')
162
if isinstance(ids, (int, long)):
165
for order in self.browse(cr, uid, ids):
166
if order.partner_id.partner_type == 'internal' and order.order_type == 'regular':
167
self.write(cr, uid, [order.id], {'order_policy': 'manual'})
168
for line in order.order_line:
169
lines.append(line.id)
170
elif order.order_type in ['donation_exp', 'donation_st', 'loan']:
171
self.write(cr, uid, [order.id], {'order_policy': 'manual'})
172
for line in order.order_line:
173
lines.append(line.id)
174
elif not order.from_yml_test:
175
self.write(cr, uid, [order.id], {'order_policy': 'manual'})
178
line_obj.write(cr, uid, lines, {'invoiced': 1})
180
return super(sale_order, self).action_wait(cr, uid, ids, args)
182
def action_purchase_order_create(self, cr, uid, ids, context={}):
184
Create a purchase order as counterpart for the loan.
186
if isinstance(ids, (int, long)):
189
purchase_obj = self.pool.get('purchase.order')
190
purchase_line_obj = self.pool.get('purchase.order.line')
191
partner_obj = self.pool.get('res.partner')
193
for order in self.browse(cr, uid, ids):
194
two_months = today() + RelativeDateTime(months=+2)
195
order_id = purchase_obj.create(cr, uid, {'partner_id': order.partner_id.id,
196
'partner_address_id': partner_obj.address_get(cr, uid, [order.partner_id.id], ['contact'])['contact'],
197
'pricelist_id': order.partner_id.property_product_pricelist_purchase.id,
199
'loan_duration': order.loan_duration,
200
'origin': order.name,
201
'order_type': 'loan',
202
'delivery_requested_date': (today() + RelativeDateTime(months=+order.loan_duration)).strftime('%Y-%m-%d'),
203
'categ': order.categ,
204
'location_id': order.shop_id.warehouse_id.lot_stock_id.id,
205
'priority': order.priority,})
206
for line in order.order_line:
207
purchase_line_obj.create(cr, uid, {'product_id': line.product_id and line.product_id.id or False,
208
'product_uom': line.product_uom.id,
209
'order_id': order_id,
210
'price_unit': line.price_unit,
211
'product_qty': line.product_uom_qty,
212
'date_planned': (today() + RelativeDateTime(months=+order.loan_duration)).strftime('%Y-%m-%d'),
214
self.write(cr, uid, [order.id], {'loan_id': order_id})
216
purchase = purchase_obj.browse(cr, uid, order_id)
218
message = _("Loan counterpart '%s' is created.") % (purchase.name,)
220
purchase_obj.log(cr, uid, order_id, message)
224
def has_stockable_products(self, cr, uid, ids, *args):
226
Override the has_stockable_product to return False
227
when the internal_type of the order is 'direct'
229
for order in self.browse(cr, uid, ids):
230
if order.order_type != 'direct':
231
return super(sale_order, self).has_stockable_product(cr, uid, ids, args)
235
#@@@override sale.sale_order.action_invoice_end
236
def action_invoice_end(self, cr, uid, ids, context=None):
238
Modified to set lines invoiced when order_type is not regular
240
for order in self.browse(cr, uid, ids, context=context):
242
# Update the sale order lines state (and invoiced flag).
244
for line in order.order_line:
247
# Check if the line is invoiced (has asociated invoice
248
# lines from non-cancelled invoices).
250
invoiced = order.noinvoice
252
for iline in line.invoice_lines:
253
if iline.invoice_id and iline.invoice_id.state != 'cancel':
32
def _hook_ship_create_stock_move(self, cr, uid, ids, move_data, order_line, *args, **kwargs):
35
def _hook_ship_create_procurement_order(self, cr, uid, ids, procurement_data, order_line, *args, **kwargs):
36
return procurement_data
38
# @@@override@sale.sale.order.action_ship_create
39
def action_ship_create(self, cr, uid, ids, *args, **kwargs):
43
wf_service = netsvc.LocalService("workflow")
45
move_obj = self.pool.get('stock.move')
46
proc_obj = self.pool.get('procurement.order')
47
company = self.pool.get('res.users').browse(cr, uid, uid).company_id
48
for order in self.browse(cr, uid, ids, context={}):
50
output_id = order.shop_id.warehouse_id.lot_output_id.id
52
for line in order.order_line:
54
date_planned = datetime.now() + relativedelta(days=line.delay or 0.0)
55
date_planned = (date_planned - timedelta(days=company.security_lead)).strftime('%Y-%m-%d %H:%M:%S')
57
if line.state == 'done':
60
if line.product_id and line.product_id.product_tmpl_id.type in ('product', 'consu') and not line.order_id.procurement_request:
61
location_id = order.shop_id.warehouse_id.lot_stock_id.id
63
pick_name = self.pool.get('ir.sequence').get(cr, uid, 'stock.picking.out')
64
picking_id = self.pool.get('stock.picking').create(cr, uid, {
69
'move_type': order.picking_policy,
71
'address_id': order.partner_shipping_id.id,
73
'invoice_state': (order.order_policy=='picking' and '2binvoiced') or 'none',
74
'company_id': order.company_id.id,
77
'name': line.name[:64],
78
'picking_id': picking_id,
79
'product_id': line.product_id.id,
81
'date_expected': date_planned,
82
'product_qty': line.product_uom_qty,
83
'product_uom': line.product_uom.id,
84
'product_uos_qty': line.product_uos_qty,
85
'product_uos': (line.product_uos and line.product_uos.id)\
86
or line.product_uom.id,
87
'product_packaging': line.product_packaging.id,
88
'address_id': line.address_allotment_id.id or order.partner_shipping_id.id,
89
'location_id': location_id,
90
'location_dest_id': output_id,
91
'sale_line_id': line.id,
96
'company_id': order.company_id.id,
98
move_data = self._hook_ship_create_stock_move(cr, uid, ids, move_data, line, *args, **kwargs)
99
move_id = self.pool.get('stock.move').create(cr, uid, move_data)
104
'origin': order.name,
105
'date_planned': date_planned,
106
'product_id': line.product_id.id,
107
'product_qty': line.product_uom_qty,
108
'product_uom': line.product_uom.id,
109
'product_uos_qty': (line.product_uos and line.product_uos_qty)\
110
or line.product_uom_qty,
111
'product_uos': (line.product_uos and line.product_uos.id)\
112
or line.product_uom.id,
113
'location_id': order.shop_id.warehouse_id.lot_stock_id.id,
114
'procure_method': line.type,
116
'property_ids': [(6, 0, [x.id for x in line.property_ids])],
117
'company_id': order.company_id.id,
119
proc_data = self._hook_ship_create_procurement_order(cr, uid, ids, proc_data, line, *args, **kwargs)
120
proc_id = self.pool.get('procurement.order').create(cr, uid, proc_data)
121
proc_ids.append(proc_id)
122
self.pool.get('sale.order.line').write(cr, uid, [line.id], {'procurement_id': proc_id})
123
if order.state == 'shipping_except':
124
for pick in order.picking_ids:
125
for move in pick.move_lines:
126
if move.state == 'cancel':
127
mov_ids = move_obj.search(cr, uid, [('state', '=', 'cancel'),('sale_line_id', '=', line.id),('picking_id', '=', pick.id)])
129
for mov in move_obj.browse(cr, uid, mov_ids):
130
move_obj.write(cr, uid, [move_id], {'product_qty': mov.product_qty, 'product_uos_qty': mov.product_uos_qty})
131
proc_obj.write(cr, uid, [proc_id], {'product_qty': mov.product_qty, 'product_uos_qty': mov.product_uos_qty})
136
wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr)
138
for proc_id in proc_ids:
139
wf_service.trg_validate(uid, 'procurement.order', proc_id, 'button_confirm', cr)
140
if order.state == 'proc_progress':
141
wf_service.trg_validate(uid, 'procurement.order', proc_id, 'button_check', cr)
143
if order.state == 'shipping_except':
144
val['state'] = 'progress'
145
val['shipped'] = False
147
if (order.order_policy == 'manual'):
148
for line in order.order_line:
149
if (not line.invoiced) and (line.state not in ('cancel', 'draft')):
150
val['state'] = 'manual'
256
if line.invoiced != invoiced:
257
vals['invoiced'] = invoiced
258
# If the line was in exception state, now it gets confirmed.
259
if line.state == 'exception':
260
vals['state'] = 'confirmed'
261
# Update the line (only when needed).
263
self.pool.get('sale.order.line').write(cr, uid, [line.id], vals, context=context)
265
# Update the sales order state.
267
if order.state == 'invoice_except':
268
self.write(cr, uid, [order.id], {'state': 'progress'}, context=context)
272
def _get_reason_type(self, cr, uid, order, context={}):
274
'regular': 'reason_type_deliver_partner',
275
'loan': 'reason_type_loan',
276
'donation_st': 'reason_type_donation',
277
'donation_exp': 'reason_type_donation_expiry',
280
if order.order_type in r_types:
281
return self.pool.get('ir.model.data').get_object_reference(cr, uid, 'reason_types_moves',r_types[order.order_type])[1]
285
def _hook_ship_create_stock_picking(self, cr, uid, ids, context=None, *args, **kwargs):
287
Please copy this to your module's method also.
288
This hook belongs to the action_ship_create method from sale>sale.py
290
- allow to modify the data for stock picking creation
292
result = super(sale_order, self)._hook_ship_create_stock_picking(cr, uid, ids, context=context, *args, **kwargs)
293
result['reason_type_id'] = self._get_reason_type(cr, uid, kwargs['order'], context)
297
def _hook_ship_create_stock_move(self, cr, uid, ids, context=None, *args, **kwargs):
299
Please copy this to your module's method also.
300
This hook belongs to the action_ship_create method from sale>sale.py
302
- allow to modify the data for stock move creation
304
result = super(sale_order, self)._hook_ship_create_stock_move(cr, uid, ids, context=context, *args, **kwargs)
305
result['reason_type_id'] = self._get_reason_type(cr, uid, kwargs['order'], context)
309
def _hook_ship_create_execute_specific_code_01(self, cr, uid, ids, context=None, *args, **kwargs):
311
Please copy this to your module's method also.
312
This hook belongs to the action_ship_create method from sale>sale.py
314
- allow to execute specific code at position 01
316
super(sale_order, self)._hook_ship_create_execute_specific_code_01(cr, uid, ids, context=context, *args, **kwargs)
317
wf_service = netsvc.LocalService("workflow")
318
order = kwargs['order']
319
proc_id = kwargs['proc_id']
320
if order.procurement_request and order.state == 'progress':
321
wf_service.trg_validate(uid, 'procurement.order', proc_id, 'button_check', cr)
325
def _hook_ship_create_line_condition(self, cr, uid, ids, context=None, *args, **kwargs):
327
Please copy this to your module's method also.
328
This hook belongs to the action_ship_create method from sale>sale.py
330
- allow to customize the execution condition
332
line = kwargs['line']
333
result = super(sale_order, self)._hook_ship_create_line_condition(cr, uid, ids, context=context, *args, **kwargs)
334
result = result and not line.order_id.procurement_request
152
self.write(cr, uid, [order.id], val)
339
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: