_inherit = ['mail.thread', 'ir.needaction_mixin']
_description = "Sales Order"
+ def onchange_shop_id(self, cr, uid, ids, shop_id, context=None):
+ v = {}
+ if shop_id:
+ shop = self.pool.get('sale.shop').browse(cr, uid, shop_id, context=context)
+ if shop.project_id.id:
+ v['project_id'] = shop.project_id.id
+ if shop.pricelist_id.id:
+ v['pricelist_id'] = shop.pricelist_id.id
+ return {'value': v}
+
def copy(self, cr, uid, id, default=None, context=None):
if not default:
default = {}
'user_id': fields.many2one('res.users', 'Salesperson', states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, select=True),
'partner_id': fields.many2one('res.partner', 'Customer', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, required=True, change_default=True, select=True),
'partner_invoice_id': fields.many2one('res.partner', 'Invoice Address', readonly=True, required=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="Invoice address for current sales order."),
- 'partner_shipping_id': fields.many2one('res.partner', 'Shipping Address', readonly=True, required=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="Shipping address for current sales order."),
+ 'partner_shipping_id': fields.many2one('res.partner', 'Delivery Address', readonly=True, required=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="Delivery address for current sales order."),
'order_policy': fields.selection([
('manual', 'On Demand'),
], 'Create Invoice', required=True, readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]},
help="""This field controls how invoice and delivery operations are synchronized.
- With 'Before Delivery', a draft invoice is created, and it must be paid before delivery."""),
'pricelist_id': fields.many2one('product.pricelist', 'Pricelist', required=True, readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="Pricelist for current sales order."),
+ 'currency_id': fields.related('pricelist_id', 'currency_id', type="many2one", relation="res.currency", readonly=True, required=True),
'project_id': fields.many2one('account.analytic.account', 'Contract/Analytic Account', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="The analytic account related to a sales order."),
'order_line': fields.one2many('sale.order.line', 'order_id', 'Order Lines', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}),
},
multi='sums', help="The total amount."),
- 'invoice_quantity': fields.selection([('order', 'Ordered Quantities')], 'Invoice on', help="The sale order will automatically create the invoice proposition (draft invoice). Ordered and delivered quantities may not be the same. You have to choose if you want your invoice based on ordered or shipped quantities. If the product is a service, shipped quantities means hours spent on the associated tasks.", required=True, readonly=True, states={'draft': [('readonly', False)]}),
+ 'invoice_quantity': fields.selection([('order', 'Ordered Quantities')], 'Invoice on', help="The sale order will automatically create the invoice proposition (draft invoice).", required=True, readonly=True, states={'draft': [('readonly', False)]}),
'payment_term': fields.many2one('account.payment.term', 'Payment Term'),
'fiscal_position': fields.many2one('account.fiscal.position', 'Fiscal Position'),
'company_id': fields.related('shop_id','company_id',type='many2one',relation='res.company',string='Company',store=True,readonly=True)
if s['state'] in ['draft', 'cancel']:
unlink_ids.append(s['id'])
else:
- raise osv.except_osv(_('Invalid action !'), _('In order to delete a confirmed sale order, you must cancel it before !'))
+ raise osv.except_osv(_('Invalid Action!'), _('In order to delete a confirmed sale order, you must cancel it before !'))
return osv.osv.unlink(self, cr, uid, unlink_ids, context=context)
- def onchange_pricelist_id(self, cr, uid, ids, pricelist_id, order_lines, context={}):
- if (not pricelist_id) or (not order_lines):
+ def onchange_pricelist_id(self, cr, uid, ids, pricelist_id, order_lines, context=None):
+ if not pricelist_id:
return {}
+ value = {
+ 'currency_id': self.pool.get('product.pricelist').browse(cr, uid, pricelist_id, context=context).currency_id.id
+ }
+ if not order_lines:
+ return {'value': value}
warning = {
'title': _('Pricelist Warning!'),
'message' : _('If you change the pricelist of this order (and eventually the currency), prices of existing order lines will not be updated.')
}
- return {'warning': warning}
-
+ return {'warning': warning, 'value': value}
def onchange_partner_id(self, cr, uid, ids, part):
if not part:
'fiscal_position': order.fiscal_position.id or order.partner_id.property_account_position.id,
'date_invoice': context.get('date_invoice', False),
'company_id': order.company_id.id,
- 'user_id': order.user_id and order.user_id.id or False,
-
+ 'user_id': order.user_id and order.user_id.id or False
}
# Care for deprecated _inv_get() hook - FIXME: to be removed after 6.1
if line.product_id and (line.product_id.type<>'service'):
return False
return True
-
- def action_invoice_create(self, cr, uid, ids, grouped=False, states=['confirmed', 'done', 'exception'], date_inv = False, context=None):
+
+ def action_invoice_create(self, cr, uid, ids, grouped=False, states=None, date_inv = False, context=None):
+ if states is None:
+ states = ['confirmed', 'done', 'exception']
res = False
invoices = {}
invoice_ids = []
This function opens a window to compose an email, with the edi sale template message loaded by default
'''
assert len(ids) == 1, 'This option should only be used for a single id at a time.'
- mod_obj = self.pool.get('ir.model.data')
- template = mod_obj.get_object_reference(cr, uid, 'sale', 'email_template_edi_sale')
- template_id = template and template[1] or False
- res = mod_obj.get_object_reference(cr, uid, 'mail', 'email_compose_message_wizard_form')
- res_id = res and res[1] or False
+ ir_model_data = self.pool.get('ir.model.data')
+ try:
+ template_id = ir_model_data.get_object_reference(cr, uid, 'sale', 'email_template_edi_sale')[1]
+ except ValueError:
+ template_id = False
+ try:
+ compose_form_id = ir_model_data.get_object_reference(cr, uid, 'mail', 'email_compose_message_wizard_form')[1]
+ except ValueError:
+ compose_form_id = False
ctx = dict(context)
ctx.update({
'default_model': 'sale.order',
'default_res_id': ids[0],
- 'default_use_template': True,
+ 'default_use_template': bool(template_id),
'default_template_id': template_id,
- })
+ 'mark_so_as_sent': True
+ })
return {
+ 'type': 'ir.actions.act_window',
'view_type': 'form',
'view_mode': 'form',
'res_model': 'mail.compose.message',
- 'views': [(res_id, 'form')],
- 'view_id': res_id,
- 'type': 'ir.actions.act_window',
+ 'views': [(compose_form_id, 'form')],
+ 'view_id': compose_form_id,
'target': 'new',
'context': ctx,
- 'nodestroy': True,
}
def action_done(self, cr, uid, ids, context=None):
self.done_send_note(cr, uid, ids, context=context)
return self.write(cr, uid, ids, {'state': 'done'}, context=context)
-
+
# ------------------------------------------------
# OpenChatter methods and notifications
# ------------------------------------------------
def cancel_send_note(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids, context=context):
self.message_post(cr, uid, [obj.id], body=_("Sale Order for <em>%s</em> <b>cancelled</b>.") % (obj.partner_id.name), context=context)
-
+
def done_send_note(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids, context=context):
- self.message_post(cr, uid, [obj.id], body=_("Sale Order for <em>%s</em> has been <b>done</b>") % (obj.partner_id.name), context=context)
+ self.message_post(cr, uid, [obj.id], body=_("Sale Order for <em>%s</em> set to <b>Done</b>") % (obj.partner_id.name), context=context)
def invoice_paid_send_note(self, cr, uid, ids, context=None):
- self.message_post(cr, uid, ids, body=_("Invoice has been <b>paid</b>."), context=context)
+ self.message_post(cr, uid, ids, body=_("Invoice <b>paid</b>."), context=context)
def invoice_send_note(self, cr, uid, ids, invoice_id, context=None):
for order in self.browse(cr, uid, ids, context=context):
_description = 'Sales Order Line'
_columns = {
'order_id': fields.many2one('sale.order', 'Order Reference', required=True, ondelete='cascade', select=True, readonly=True, states={'draft':[('readonly',False)]}),
- 'name': fields.text('Product Description', size=256, required=True, select=True, readonly=True, states={'draft': [('readonly', False)]}),
+ 'name': fields.text('Description', size=256, required=True, select=True, readonly=True, states={'draft': [('readonly', False)]}),
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of sales order lines."),
'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok', '=', True)], change_default=True),
'invoice_lines': fields.many2many('account.invoice.line', 'sale_order_line_invoice_rel', 'order_line_id', 'invoice_id', 'Invoice Lines', readonly=True),
'invoiced': fields.boolean('Invoiced', readonly=True),
'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Product Price'), readonly=True, states={'draft': [('readonly', False)]}),
+ 'type': fields.selection([('make_to_stock', 'from stock'), ('make_to_order', 'on order')], 'Procurement Method', required=True, readonly=True, states={'draft': [('readonly', False)]},
+ help="If 'on order', it triggers a procurement when the sale order is confirmed to create a task, purchase order or manufacturing order linked to this sale order line."),
'price_subtotal': fields.function(_amount_line, string='Subtotal', digits_compute= dp.get_precision('Account')),
'tax_id': fields.many2many('account.tax', 'sale_order_tax', 'order_line_id', 'tax_id', 'Taxes', readonly=True, states={'draft': [('readonly', False)]}),
'address_allotment_id': fields.many2one('res.partner', 'Allotment Partner'),
'product_uom': fields.many2one('product.uom', 'Unit of Measure ', required=True, readonly=True, states={'draft': [('readonly', False)]}),
'product_uos_qty': fields.float('Quantity (UoS)' ,digits_compute= dp.get_precision('Product UoS'), readonly=True, states={'draft': [('readonly', False)]}),
'product_uos': fields.many2one('product.uom', 'Product UoS'),
- 'discount': fields.float('Discount (%)', digits_compute= dp.get_precision('Discount'), readonly=True, states={'draft': [('readonly', False)]}),
+ 'discount': fields.float('Discount (%)', digits_compute= dp.get_precision('Discount'), readonly=True, states={'draft': [('readonly', False)]}),
'th_weight': fields.float('Weight', readonly=True, states={'draft': [('readonly', False)]}),
'state': fields.selection([('cancel', 'Cancelled'),('draft', 'Draft'),('confirmed', 'Confirmed'),('exception', 'Exception'),('done', 'Done')], 'Status', required=True, readonly=True,
help='* The \'Draft\' state is set when the related sales order in draft state. \
\n* The \'Cancelled\' state is set when a user cancel the sales order related.'),
'order_partner_id': fields.related('order_id', 'partner_id', type='many2one', relation='res.partner', store=True, string='Customer'),
'salesman_id':fields.related('order_id', 'user_id', type='many2one', relation='res.users', store=True, string='Salesperson'),
- 'company_id': fields.related('order_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True),
-
+ 'company_id': fields.related('order_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True),
}
_order = 'sequence, id'
_defaults = {
'sequence': 10,
'invoiced': 0,
'state': 'draft',
+ 'type': 'make_to_stock',
'price_unit': 0.0,
}
+ def _get_line_qty(self, cr, uid, line, context=None):
+ if (line.order_id.invoice_quantity=='order'):
+ if line.product_uos:
+ return line.product_uos_qty or 0.0
+ return line.product_uom_qty
+
+ def _get_line_uom(self, cr, uid, line, context=None):
+ if (line.order_id.invoice_quantity=='order'):
+ if line.product_uos:
+ return line.product_uos.id
+ return line.product_uom.id
+
def _prepare_order_line_invoice_line(self, cr, uid, line, account_id=False, context=None):
"""Prepare the dict of values to create the new invoice line for a
sale order line. This method may be overridden to implement custom
:return: dict of values to create() the invoice line
"""
res = {}
- def _get_line_qty(line):
- if (line.order_id.invoice_quantity=='order'):
- if line.product_uos:
- return line.product_uos_qty or 0.0
- return line.product_uom_qty
-
- def _get_line_uom(line):
- if (line.order_id.invoice_quantity=='order'):
- if line.product_uos:
- return line.product_uos.id
- return line.product_uom.id
if not line.invoiced:
if not account_id:
if line.product_id:
'property_account_income_categ', 'product.category',
context=context)
account_id = prop and prop.id or False
- uosqty = _get_line_qty(line)
- uos_id = _get_line_uom(line)
+ uosqty = self._get_line_qty(cr, uid, line, context=context)
+ uos_id = self._get_line_uom(cr, uid, line, context=context)
pu = 0.0
if uosqty:
pu = round(line.price_unit * line.product_uom_qty / uosqty,
_('There is no Fiscal Position defined or Income category account defined for default properties of Product categories.'))
res = {
'name': line.name,
+ 'sequence': line.sequence,
'origin': line.order_id.name,
'account_id': account_id,
'price_unit': pu,
def button_cancel(self, cr, uid, ids, context=None):
for line in self.browse(cr, uid, ids, context=context):
if line.invoiced:
- raise osv.except_osv(_('Invalid action !'), _('You cannot cancel a sale order line that has already been invoiced!'))
+ raise osv.except_osv(_('Invalid Action!'), _('You cannot cancel a sale order line that has already been invoiced.'))
return self.write(cr, uid, ids, {'state': 'cancel'})
def button_confirm(self, cr, uid, ids, context=None):
context_partner = {'lang': lang, 'partner_id': partner_id}
if not product:
- return {'value': {'th_weight': 0,
+ return {'value': {'th_weight': 0,
'product_uos_qty': qty}, 'domain': {'product_uom': [],
'product_uos': []}}
if not date_order:
date_order = time.strftime(DEFAULT_SERVER_DATE_FORMAT)
-#
+
result = {}
warning_msgs = {}
product_obj = product_obj.browse(cr, uid, product, context=context)
fpos = fiscal_position and self.pool.get('account.fiscal.position').browse(cr, uid, fiscal_position) or False
if update_tax: #The quantity only have changed
result['tax_id'] = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos, product_obj.taxes_id)
- result.update({'type': product_obj.procure_method})
if not flag:
result['name'] = self.pool.get('product.product').name_get(cr, uid, [product_obj.id], context=context_partner)[0][1]
sale_order_line()
-class mail_message(osv.osv):
- _inherit = 'mail.message'
-
- def _postprocess_sent_message(self, cr, uid, message, context=None):
- if message.model == 'sale.order':
+class mail_compose_message(osv.osv):
+ _inherit = 'mail.compose.message'
+ def send_mail(self, cr, uid, ids, context=None):
+ context = context or {}
+ if context.get('mark_so_as_sent', False) and context.get('default_res_id', False):
wf_service = netsvc.LocalService("workflow")
- wf_service.trg_validate(uid, 'sale.order', message.res_id, 'quotation_sent', cr)
- return super(mail_message, self)._postprocess_sent_message(cr, uid, message=message, context=context)
+ wf_service.trg_validate(uid, 'sale.order', context.get('default_res_id', False), 'quotation_sent', cr)
+ return super(mail_compose_message, self).send_mail(cr, uid, ids, context=context)
-mail_message()
+mail_compose_message()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: