from osv.orm import browse_record, browse_null
from tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT, DATETIME_FORMATS_MAP
-#
-# Model definition
-#
class purchase_order(osv.osv):
def _amount_all(self, cr, uid, ids, field_name, arg, context=None):
cur = order.pricelist_id.currency_id
for line in order.order_line:
val1 += line.price_subtotal
- for c in self.pool.get('account.tax').compute_all(cr, uid, line.taxes_id, line.price_unit, line.product_qty, line.product_id.id, order.partner_id)['taxes']:
+ for c in self.pool.get('account.tax').compute_all(cr, uid, line.taxes_id, line.price_unit, line.product_qty, line.product_id, order.partner_id)['taxes']:
val += c.get('amount', 0.0)
res[order.id]['amount_tax']=cur_obj.round(cr, uid, cur, val)
res[order.id]['amount_untaxed']=cur_obj.round(cr, uid, cur, val1)
invoiced = True
res[purchase.id] = invoiced
return res
+
+ def _get_journal(self, cr, uid, context=None):
+ if context is None:
+ context = {}
+ user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
+ company_id = context.get('company_id', user.company_id.id)
+ journal_obj = self.pool.get('account.journal')
+ res = journal_obj.search(cr, uid, [('type', '=', 'purchase'),
+ ('company_id', '=', company_id)],
+ limit=1)
+ return res and res[0] or False
STATE_SELECTION = [
('draft', 'Draft PO'),
- ('wait', 'Waiting'),
('sent', 'RFQ Sent'),
('confirmed', 'Waiting Approval'),
('approved', 'Purchase Order'),
_columns = {
'name': fields.char('Order Reference', size=64, required=True, select=True, help="Unique number of the purchase order, computed automatically when the purchase order is created."),
'origin': fields.char('Source Document', size=64,
- help="Reference of the document that generated this purchase order request."
+ help="Reference of the document that generated this purchase order request; a sale order or an internal procurement request."
),
- 'partner_ref': fields.char('Supplier Reference', states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]}, size=64),
+ 'partner_ref': fields.char('Supplier Reference', states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]}, size=64,
+ help="Reference of the sale order or quotation sent by your supplier. It's mainly used to do the matching when you receive the products as this reference is usually written on the delivery order sent by your supplier."),
'date_order':fields.date('Order Date', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)]}, select=True, help="Date on which this document has been created."),
'date_approve':fields.date('Date Approved', readonly=1, select=True, help="Date on which purchase order has been approved"),
'partner_id':fields.many2one('res.partner', 'Supplier', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]}, change_default=True),
help="Put an address if you want to deliver directly from the supplier to the customer. " \
"Otherwise, keep empty to deliver to your own company."
),
- 'warehouse_id': fields.many2one('stock.warehouse', 'Destination Warehouse', states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]}),
- 'location_id': fields.many2one('stock.location', 'Destination', required=True, domain=[('usage','<>','view')]),
+ 'warehouse_id': fields.many2one('stock.warehouse', 'Destination Warehouse'),
+ 'location_id': fields.many2one('stock.location', 'Destination', required=True, domain=[('usage','<>','view')], states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]} ),
'pricelist_id':fields.many2one('product.pricelist', 'Pricelist', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]}, help="The pricelist sets the currency used for this purchase order. It also computes the supplier price for the selected products/quantities."),
- 'state': fields.selection(STATE_SELECTION, 'Status', readonly=True, help="The state of the purchase order or the quotation request. A quotation is a purchase order in a 'Draft' state. Then the order has to be confirmed by the user, the state switch to 'Confirmed'. Then the supplier must confirm the order to change the state to 'Approved'. When the purchase order is paid and received, the state becomes 'Done'. If a cancel action occurs in the invoice or in the reception of goods, the state becomes in exception.", select=True),
+ 'currency_id': fields.related('pricelist_id', 'currency_id', type="many2one", relation="res.currency", readonly=True, required=True),
+ 'state': fields.selection(STATE_SELECTION, 'Status', readonly=True, help="The status of the purchase order or the quotation request. A quotation is a purchase order in a 'Draft' status. Then the order has to be confirmed by the user, the status switch to 'Confirmed'. Then the supplier must confirm the order to change the status to 'Approved'. When the purchase order is paid and received, the status becomes 'Done'. If a cancel action occurs in the invoice or in the reception of goods, the status becomes in exception.", select=True),
'order_line': fields.one2many('purchase.order.line', 'order_id', 'Order Lines', states={'approved':[('readonly',True)],'done':[('readonly',True)]}),
'validator' : fields.many2one('res.users', 'Validated by', readonly=True),
'notes': fields.text('Terms and Conditions'),
'shipped_rate': fields.function(_shipped_rate, string='Received', type='float'),
'invoiced': fields.function(_invoiced, string='Invoice Received', type='boolean', help="It indicates that an invoice has been paid"),
'invoiced_rate': fields.function(_invoiced_rate, string='Invoiced', type='float'),
- 'invoice_method': fields.selection([('manual','Based on Purchase Order lines'),('order','Based on generated draft invoice'),('picking','Bases on incoming shipments')], 'Invoicing Control', required=True,
+ 'invoice_method': fields.selection([('manual','Based on Purchase Order lines'),('order','Based on generated draft invoice'),('picking','Based on incoming shipments')], 'Invoicing Control', required=True,
+ readonly=True, states={'draft':[('readonly',False)], 'sent':[('readonly',False)]},
help="Based on Purchase Order lines: place individual lines in 'Invoice Control > Based on P.O. lines' from where you can selectively create an invoice.\n" \
"Based on generated invoice: create a draft invoice you can validate later.\n" \
"Bases on incoming shipments: let you create an invoice when receptions are validated."
'purchase.order.line': (_get_order, ['date_planned'], 10),
}
),
- 'amount_untaxed': fields.function(_amount_all, digits_compute= dp.get_precision('Purchase Price'), string='Untaxed Amount',
+ 'amount_untaxed': fields.function(_amount_all, digits_compute= dp.get_precision('Account'), string='Untaxed Amount',
store={
'purchase.order.line': (_get_order, None, 10),
}, multi="sums", help="The amount without tax"),
- 'amount_tax': fields.function(_amount_all, digits_compute= dp.get_precision('Purchase Price'), string='Taxes',
+ 'amount_tax': fields.function(_amount_all, digits_compute= dp.get_precision('Account'), string='Taxes',
store={
'purchase.order.line': (_get_order, None, 10),
}, multi="sums", help="The tax amount"),
- 'amount_total': fields.function(_amount_all, digits_compute= dp.get_precision('Purchase Price'), string='Total',
+ 'amount_total': fields.function(_amount_all, digits_compute= dp.get_precision('Account'), string='Total',
store={
'purchase.order.line': (_get_order, None, 10),
}, multi="sums",help="The total amount"),
'payment_term': fields.many2one('account.payment.term', 'Payment Term'),
'product_id': fields.related('order_line','product_id', type='many2one', relation='product.product', string='Product'),
'create_uid': fields.many2one('res.users', 'Responsible'),
- 'company_id': fields.many2one('res.company','Company',required=True,select=1),
+ 'company_id': fields.many2one('res.company','Company',required=True,select=1, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)]}),
+ 'journal_id': fields.many2one('account.journal', 'Journal'),
}
_defaults = {
'date_order': fields.date.context_today,
'state': 'draft',
- 'name': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'purchase.order'),
+ 'name': lambda obj, cr, uid, context: '/',
'shipped': 0,
'invoice_method': 'order',
'invoiced': 0,
'pricelist_id': lambda self, cr, uid, context: context.get('partner_id', False) and self.pool.get('res.partner').browse(cr, uid, context['partner_id']).property_product_pricelist_purchase.id,
'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'purchase.order', context=c),
+ 'journal_id': _get_journal,
}
_sql_constraints = [
('name_uniq', 'unique(name, company_id)', 'Order Reference must be unique per Company!'),
]
_name = "purchase.order"
- _inherit = ['ir.needaction_mixin', 'mail.thread']
+ _inherit = ['mail.thread', 'ir.needaction_mixin']
_description = "Purchase Order"
_order = "name desc"
-
+
def create(self, cr, uid, vals, context=None):
+ if vals.get('name','/')=='/':
+ vals['name'] = self.pool.get('ir.sequence').get(cr, uid, 'purchase.order') or '/'
order = super(purchase_order, self).create(cr, uid, vals, context=context)
if order:
self.create_send_note(cr, uid, [order], context=context)
return order
-
+
def unlink(self, cr, uid, ids, context=None):
purchase_orders = self.read(cr, uid, ids, ['state'], context=context)
unlink_ids = []
if s['state'] in ['draft','cancel']:
unlink_ids.append(s['id'])
else:
- raise osv.except_osv(_('Invalid action !'), _('In order to delete a purchase order, it must be cancelled first!'))
+ raise osv.except_osv(_('Invalid Action!'), _('In order to delete a purchase order, you must cancel it first.'))
- # TODO: temporary fix in 5.0, to remove in 5.2 when subflows support
# automatically sending subflow.delete upon deletion
wf_service = netsvc.LocalService("workflow")
for id in unlink_ids:
def button_dummy(self, cr, uid, ids, context=None):
return True
+ def onchange_pricelist(self, cr, uid, ids, pricelist_id, context=None):
+ if not pricelist_id:
+ return {}
+ return {'value': {'currency_id': self.pool.get('product.pricelist').browse(cr, uid, pricelist_id, context=context).currency_id.id}}
+
def onchange_dest_address_id(self, cr, uid, ids, address_id):
if not address_id:
return {}
'payment_term': supplier.property_supplier_payment_term.id or False,
}}
+ def invoice_open(self, cr, uid, ids, context=None):
+ mod_obj = self.pool.get('ir.model.data')
+ act_obj = self.pool.get('ir.actions.act_window')
+
+ result = mod_obj.get_object_reference(cr, uid, 'account', 'action_invoice_tree2')
+ id = result and result[1] or False
+ result = act_obj.read(cr, uid, [id], context=context)[0]
+ inv_ids = []
+ for po in self.browse(cr, uid, ids, context=context):
+ inv_ids+= [invoice.id for invoice in po.invoice_ids]
+ if not inv_ids:
+ raise osv.except_osv(_('Error!'), _('Please create Invoices.'))
+ #choose the view_mode accordingly
+ if len(inv_ids)>1:
+ result['domain'] = "[('id','in',["+','.join(map(str, inv_ids))+"])]"
+ else:
+ res = mod_obj.get_object_reference(cr, uid, 'account', 'invoice_supplier_form')
+ result['views'] = [(res and res[1] or False, 'form')]
+ result['res_id'] = inv_ids and inv_ids[0] or False
+ return result
+
def view_invoice(self, cr, uid, ids, context=None):
'''
This function returns an action that display existing invoices of given sale order ids. It can either be a in a list or in a form view, if there is only one invoice to show.
if not po.invoice_ids:
context.update({'active_ids' : [line.id for line in po.order_line]})
wizard_obj.makeInvoices(cr, uid, [], context=context)
-
+
for po in self.browse(cr, uid, ids, context=context):
inv_ids+= [invoice.id for invoice in po.invoice_ids]
res = mod_obj.get_object_reference(cr, uid, 'account', 'invoice_supplier_form')
for po in self.browse(cr, uid, ids, context=context):
pick_ids += [picking.id for picking in po.picking_ids]
- res = mod_obj.get_object_reference(cr, uid, 'stock', 'view_picking_in_form')
- res_id = res and res[1] or False
-
- return {
- 'name': _('Receptions'),
- 'view_type': 'form',
- 'view_mode': 'form',
- 'view_id': [res_id],
- 'res_model': 'stock.picking',
- 'context': "{'contact_display': 'partner'}",
- 'type': 'ir.actions.act_window',
- 'nodestroy': True,
- 'target': 'current',
- 'res_id': pick_ids and pick_ids[0] or False,
- }
+ action_model, action_id = tuple(mod_obj.get_object_reference(cr, uid, 'stock', 'action_picking_tree4'))
+ action = self.pool.get(action_model).read(cr, uid, action_id, context=context)
+ ctx = eval(action['context'])
+ ctx.update({
+ 'search_default_purchase_id': ids[0]
+ })
+ if pick_ids and len(pick_ids) == 1:
+ form_view_ids = [view_id for view_id, view in action['views'] if view == 'form']
+ view_id = form_view_ids and form_view_ids[0] or False
+ action.update({
+ 'views': [],
+ 'view_mode': 'form',
+ 'view_id': view_id,
+ 'res_id': pick_ids[0]
+ })
+
+ action.update({
+ 'context': ctx,
+ })
+ return action
def wkf_approve_order(self, cr, uid, ids, context=None):
self.write(cr, uid, ids, {'state': 'approved', 'date_approve': fields.date.context_today(self,cr,uid,context=context)})
'''
This function opens a window to compose an email, with the edi purchase template message loaded by default
'''
- mod_obj = self.pool.get('ir.model.data')
- template = mod_obj.get_object_reference(cr, uid, 'purchase', 'email_template_edi_purchase')
- 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
- ctx = dict(context, active_model='purchase.order', active_id=ids[0])
- ctx.update({'mail.compose.template_id': template_id})
+ ir_model_data = self.pool.get('ir.model.data')
+ try:
+ template_id = ir_model_data.get_object_reference(cr, uid, 'purchase', 'email_template_edi_purchase')[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': 'purchase.order',
+ 'default_res_id': ids[0],
+ 'default_use_template': bool(template_id),
+ 'default_template_id': template_id,
+ 'default_composition_mode': 'comment',
+ })
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 print_quotation(self, cr, uid, ids, context=None):
+ '''
+ This function prints the request for quotation and mark it as sent, so that we can see more easily the next step of the workflow
+ '''
+ assert len(ids) == 1, 'This option should only be used for a single id at a time'
+ wf_service = netsvc.LocalService("workflow")
+ wf_service.trg_validate(uid, 'purchase.order', ids[0], 'send_rfq', cr)
+ datas = {
+ 'model': 'purchase.order',
+ 'ids': ids,
+ 'form': self.read(cr, uid, ids[0], context=context),
+ }
+ return {'type': 'ir.actions.report.xml', 'report_name': 'purchase.quotation', 'datas': datas, 'nodestroy': True}
+
#TODO: implement messages system
def wkf_confirm_order(self, cr, uid, ids, context=None):
todo = []
for po in self.browse(cr, uid, ids, context=context):
if not po.order_line:
- raise osv.except_osv(_('Error !'),_('You cannot confirm a purchase order without any lines.'))
+ raise osv.except_osv(_('Error!'),_('You cannot confirm a purchase order without any purchase order line.'))
for line in po.order_line:
if line.state=='draft':
todo.append(line.id)
-# current_name = self.name_get(cr, uid, ids)[0][1]
+
self.pool.get('purchase.order.line').action_confirm(cr, uid, todo, context)
for id in ids:
self.write(cr, uid, [id], {'state' : 'confirmed', 'validator' : uid})
pay_acc_id = order.partner_id.property_account_payable.id
journal_ids = journal_obj.search(cr, uid, [('type', '=','purchase'),('company_id', '=', order.company_id.id)], limit=1)
if not journal_ids:
- raise osv.except_osv(_('Error !'),
- _('There is no purchase journal defined for this company: "%s" (id:%d)') % (order.company_id.name, order.company_id.id))
+ raise osv.except_osv(_('Error!'),
+ _('Define purchase journal for this company: "%s" (id:%d).') % (order.company_id.name, order.company_id.id))
# generate invoice line correspond to PO line and link that to created invoice (inv_id) and PO line
inv_lines = []
if not acc_id:
acc_id = po_line.product_id.categ_id.property_account_expense_categ.id
if not acc_id:
- raise osv.except_osv(_('Error !'), _('There is no expense account defined for this product: "%s" (id:%d)') % (po_line.product_id.name, po_line.product_id.id,))
+ raise osv.except_osv(_('Error!'), _('Define expense account for this company: "%s" (id:%d).') % (po_line.product_id.name, po_line.product_id.id,))
else:
acc_id = property_obj.get(cr, uid, 'property_account_expense_categ', 'product.category').id
fpos = order.fiscal_position or False
if res:
self.invoice_send_note(cr, uid, ids, res, context)
return res
-
+
def invoice_done(self, cr, uid, ids, context=None):
self.write(cr, uid, ids, {'state':'approved'}, context=context)
self.invoice_done_send_note(cr, uid, ids, context=context)
return True
-
- def has_stockable_product(self,cr, uid, ids, *args):
+
+ def has_stockable_product(self, cr, uid, ids, *args):
for order in self.browse(cr, uid, ids):
for order_line in order.order_line:
if order_line.product_id and order_line.product_id.product_tmpl_id.type in ('product', 'consu'):
for pick in purchase.picking_ids:
if pick.state not in ('draft','cancel'):
raise osv.except_osv(
- _('Unable to cancel this purchase order!'),
- _('You must first cancel all receptions related to this purchase order.'))
+ _('Unable to cancel this purchase order.'),
+ _('First cancel all receptions related to this purchase order.'))
for pick in purchase.picking_ids:
wf_service.trg_validate(uid, 'stock.picking', pick.id, 'button_cancel', cr)
for inv in purchase.invoice_ids:
if inv and inv.state not in ('cancel','draft'):
raise osv.except_osv(
- _('Unable to cancel this purchase order!'),
- _('You must first cancel all invoices related to this purchase order.'))
+ _('Unable to cancel this purchase order.'),
+ _('You must first cancel all receptions related to this purchase order.'))
if inv:
wf_service.trg_validate(uid, 'account.invoice', inv.id, 'invoice_cancel', cr)
self.write(cr,uid,ids,{'state':'cancel'})
'invoice_state': '2binvoiced' if order.invoice_method == 'picking' else 'none',
'type': 'in',
'partner_id': order.dest_address_id.id or order.partner_id.id,
- 'invoice_state': '2binvoiced' if order.invoice_method == 'picking' else 'none',
'purchase_id': order.id,
'company_id': order.company_id.id,
'move_lines' : [],
'partner_id': order.dest_address_id.id or order.partner_id.id,
'move_dest_id': order_line.move_dest_id.id,
'state': 'draft',
+ 'type':'in',
'purchase_line_id': order_line.id,
'company_id': order.company_id.id,
'price_unit': order_line.price_unit
wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr)
return [picking_id]
- def action_picking_create(self,cr, uid, ids, context=None):
+ def action_picking_create(self, cr, uid, ids, context=None):
picking_ids = []
for order in self.browse(cr, uid, ids):
picking_ids.extend(self._create_pickings(cr, uid, order, order.order_line, None, context=context))
list_key.sort()
return tuple(list_key)
- # compute what the new orders should contain
+ # Compute what the new orders should contain
new_orders = {}
wf_service.trg_redirect(uid, 'purchase.order', old_id, neworder_id, cr)
wf_service.trg_validate(uid, 'purchase.order', old_id, 'purchase_cancel', cr)
return orders_info
-
+
# --------------------------------------
# OpenChatter methods and notifications
# --------------------------------------
-
- def get_needaction_user_ids(self, cr, uid, ids, context=None):
- result = super(purchase_order, self).get_needaction_user_ids(cr, uid, ids, context=context)
- for obj in self.browse(cr, uid, ids, context=context):
- if obj.state == 'approved':
- result[obj.id].append(obj.validator.id)
- return result
-
+
+ def needaction_domain_get(self, cr, uid, ids, context=None):
+ return [('state', '=', 'draft')]
+
def create_send_note(self, cr, uid, ids, context=None):
- return self.message_append_note(cr, uid, ids, body=_("Request for quotation <b>created</b>."), context=context)
+ return self.message_post(cr, uid, ids, body=_("Request for quotation <b>created</b>."), context=context)
def confirm_send_note(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids, context=context):
- self.message_subscribe(cr, uid, [obj.id], [obj.validator.id], context=context)
- self.message_append_note(cr, uid, [obj.id], body=_("Quotation for <em>%s</em> <b>converted</b> to a Purchase Order of %s %s.") % (obj.partner_id.name, obj.amount_total, obj.pricelist_id.currency_id.symbol), context=context)
-
+ self.message_post(cr, uid, [obj.id], body=_("Quotation for <em>%s</em> <b>converted</b> to a Purchase Order of %s %s.") % (obj.partner_id.name, obj.amount_total, obj.pricelist_id.currency_id.symbol), context=context)
+
def shipment_send_note(self, cr, uid, ids, picking_id, context=None):
for order in self.browse(cr, uid, ids, context=context):
for picking in (pck for pck in order.picking_ids if pck.id == picking_id):
# convert it to the user TZ and re-render it with %Z to add the timezone
picking_datetime = fields.DT.datetime.strptime(picking.min_date, DEFAULT_SERVER_DATETIME_FORMAT)
picking_date_str = fields.datetime.context_timestamp(cr, uid, picking_datetime, context=context).strftime(DATETIME_FORMATS_MAP['%+'] + " (%Z)")
- self.message_append_note(cr, uid, [order.id], body=_("Shipment <em>%s</em> <b>scheduled</b> for %s.") % (picking.name, picking_date_str), context=context)
-
+ self.message_post(cr, uid, [order.id], body=_("Shipment <em>%s</em> <b>scheduled</b> for %s.") % (picking.name, picking_date_str), context=context)
+
def invoice_send_note(self, cr, uid, ids, invoice_id, context=None):
for order in self.browse(cr, uid, ids, context=context):
for invoice in (inv for inv in order.invoice_ids if inv.id == invoice_id):
- self.message_append_note(cr, uid, [order.id], body=_("Draft Invoice of %s %s is <b>waiting for validation</b>.") % (invoice.amount_total, invoice.currency_id.symbol), context=context)
-
+ self.message_post(cr, uid, [order.id], body=_("Draft Invoice of %s %s is <b>waiting for validation</b>.") % (invoice.amount_total, invoice.currency_id.symbol), context=context)
+
def shipment_done_send_note(self, cr, uid, ids, context=None):
- self.message_append_note(cr, uid, ids, body=_("""Shipment <b>received</b>."""), context=context)
-
+ self.message_post(cr, uid, ids, body=_("""Shipment <b>received</b>."""), context=context)
+
def invoice_done_send_note(self, cr, uid, ids, context=None):
- self.message_append_note(cr, uid, ids, body=_("Invoice <b>paid</b>."), context=context)
-
+ self.message_post(cr, uid, ids, body=_("Invoice <b>paid</b>."), context=context)
+
def draft_send_note(self, cr, uid, ids, context=None):
- return self.message_append_note(cr, uid, ids, body=_("Purchase Order has been set to <b>draft</b>."), context=context)
-
+ return self.message_post(cr, uid, ids, body=_("Purchase Order has been set to <b>draft</b>."), context=context)
+
def cancel_send_note(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids, context=context):
- self.message_append_note(cr, uid, [obj.id], body=_("Purchase Order for <em>%s</em> <b>cancelled</b>.") % (obj.partner_id.name), context=context)
+ self.message_post(cr, uid, [obj.id], body=_("Purchase Order for <em>%s</em> <b>cancelled</b>.") % (obj.partner_id.name), context=context)
purchase_order()
cur_obj=self.pool.get('res.currency')
tax_obj = self.pool.get('account.tax')
for line in self.browse(cr, uid, ids, context=context):
- taxes = tax_obj.compute_all(cr, uid, line.taxes_id, line.price_unit, line.product_qty)
+ taxes = tax_obj.compute_all(cr, uid, line.taxes_id, line.price_unit, line.product_qty, line.product_id, line.order_id.partner_id)
cur = line.order_id.pricelist_id.currency_id
res[line.id] = cur_obj.round(cr, uid, cur, taxes['total'])
return res
- def _get_uom_id(self, cr, uid, context=None):
- try:
- proxy = self.pool.get('ir.model.data')
- result = proxy.get_object_reference(cr, uid, 'product', 'product_uom_unit')
- return result[1]
- except Exception, ex:
- return False
-
_columns = {
'name': fields.text('Description', required=True),
'product_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True),
'product_id': fields.many2one('product.product', 'Product', domain=[('purchase_ok','=',True)], change_default=True),
'move_ids': fields.one2many('stock.move', 'purchase_line_id', 'Reservation', readonly=True, ondelete='set null'),
'move_dest_id': fields.many2one('stock.move', 'Reservation Destination', ondelete='set null'),
- 'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Purchase Price')),
- 'price_subtotal': fields.function(_amount_line, string='Subtotal', digits_compute= dp.get_precision('Purchase Price')),
+ 'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Product Price')),
+ 'price_subtotal': fields.function(_amount_line, string='Subtotal', digits_compute= dp.get_precision('Account')),
'order_id': fields.many2one('purchase.order', 'Order Reference', select=True, required=True, ondelete='cascade'),
'account_analytic_id':fields.many2one('account.analytic.account', 'Analytic Account',),
'company_id': fields.related('order_id','company_id',type='many2one',relation='res.company',string='Company', store=True, readonly=True),
'state': fields.selection([('draft', 'Draft'), ('confirmed', 'Confirmed'), ('done', 'Done'), ('cancel', 'Cancelled')], 'Status', required=True, readonly=True,
- help=' * The \'Draft\' state is set automatically when purchase order in draft state. \
- \n* The \'Confirmed\' state is set automatically as confirm when purchase order in confirm state. \
- \n* The \'Done\' state is set automatically when purchase order is set as done. \
- \n* The \'Cancelled\' state is set automatically when user cancel purchase order.'),
+ help=' * The \'Draft\' status is set automatically when purchase order in draft status. \
+ \n* The \'Confirmed\' status is set automatically as confirm when purchase order in confirm status. \
+ \n* The \'Done\' status is set automatically when purchase order is set as done. \
+ \n* The \'Cancelled\' status is set automatically when user cancel purchase order.'),
'invoice_lines': fields.many2many('account.invoice.line', 'purchase_order_line_invoice_rel', 'order_line_id', 'invoice_id', 'Invoice Lines', readonly=True),
'invoiced': fields.boolean('Invoiced', readonly=True),
'partner_id': fields.related('order_id','partner_id',string='Partner',readonly=True,type="many2one", relation="res.partner", store=True),
}
_defaults = {
- 'product_uom' : _get_uom_id,
'product_qty': lambda *a: 1.0,
'state': lambda *args: 'draft',
'invoiced': lambda *a: 0,
supplier_delay = int(supplier_info.delay) if supplier_info else 0
return datetime.strptime(date_order_str, DEFAULT_SERVER_DATE_FORMAT) + relativedelta(days=supplier_delay)
+ def _check_product_uom_group(self, cr, uid, context=None):
+ group_uom = self.pool.get('ir.model.data').get_object(cr, uid, 'product', 'group_uom')
+ res = [user for user in group_uom.users if user.id == uid]
+ return len(res) and True or False
+
+
def onchange_product_id(self, cr, uid, ids, pricelist_id, product_id, qty, uom_id,
partner_id, date_order=False, fiscal_position_id=False, date_planned=False,
name=False, price_unit=False, context=None):
account_tax = self.pool.get('account.tax')
# - check for the presence of partner_id and pricelist_id
- if not pricelist_id:
- raise osv.except_osv(_('No Pricelist !'), _('You have to select a pricelist or a supplier in the purchase form !\nPlease set one before choosing a product.'))
- if not partner_id:
- raise osv.except_osv(_('No Partner!'), _('You have to select a partner in the purchase form !\nPlease set one partner before choosing a product.'))
+ #if not partner_id:
+ # raise osv.except_osv(_('No Partner!'), _('Select a partner in purchase order to choose a product.'))
+ #if not pricelist_id:
+ # raise osv.except_osv(_('No Pricelist !'), _('Select a price list in the purchase order form before choosing a product.'))
# - determine name and notes based on product in partner lang.
- lang = res_partner.browse(cr, uid, partner_id).lang
- context_partner = {'lang': lang, 'partner_id': partner_id}
+ context_partner = context.copy()
+ if partner_id:
+ lang = res_partner.browse(cr, uid, partner_id).lang
+ context_partner.update( {'lang': lang, 'partner_id': partner_id} )
product = product_product.browse(cr, uid, product_id, context=context_partner)
name = product.name
if product.description_purchase:
uom_id = product_uom_po_id
if product.uom_id.category_id.id != product_uom.browse(cr, uid, uom_id, context=context).category_id.id:
- res['warning'] = {'title': _('Warning'), 'message': _('Selected Unit of Measure does not belong to the same category as the product Unit of Measure')}
+ if self._check_product_uom_group(cr, uid, context=context):
+ res['warning'] = {'title': _('Warning!'), 'message': _('Selected Unit of Measure does not belong to the same category as the product Unit of Measure.')}
uom_id = product_uom_po_id
res['value'].update({'product_uom': uom_id})
qty = qty or 1.0
supplierinfo = False
for supplier in product.seller_ids:
- if supplier.name.id == partner_id:
+ if partner_id and (supplier.name.id == partner_id):
supplierinfo = supplier
if supplierinfo.product_uom.id != uom_id:
- res['warning'] = {'title': _('Warning'), 'message': _('The selected supplier only sells this product by %s') % supplierinfo.product_uom.name }
+ res['warning'] = {'title': _('Warning!'), 'message': _('The selected supplier only sells this product by %s') % supplierinfo.product_uom.name }
min_qty = product_uom._compute_qty(cr, uid, supplierinfo.product_uom.id, supplierinfo.min_qty, to_uom_id=uom_id)
if qty < min_qty: # If the supplier quantity is greater than entered from user, set minimal.
- res['warning'] = {'title': _('Warning'), 'message': _('The selected supplier has a minimal quantity set to %s %s, you should not purchase less.') % (supplierinfo.min_qty, supplierinfo.product_uom.name)}
+ res['warning'] = {'title': _('Warning!'), 'message': _('The selected supplier has a minimal quantity set to %s %s, you should not purchase less.') % (supplierinfo.min_qty, supplierinfo.product_uom.name)}
qty = min_qty
dt = self._get_date_planned(cr, uid, supplierinfo, date_order, context=context).strftime(DEFAULT_SERVER_DATETIME_FORMAT)
res['value'].update({'date_planned': date_planned or dt, 'product_qty': qty})
# - determine price_unit and taxes_id
- price = product_pricelist.price_get(cr, uid, [pricelist_id],
- product.id, qty or 1.0, partner_id, {'uom': uom_id, 'date': date_order})[pricelist_id]
+ if pricelist_id:
+ price = product_pricelist.price_get(cr, uid, [pricelist_id],
+ product.id, qty or 1.0, partner_id or False, {'uom': uom_id, 'date': date_order})[pricelist_id]
+ else:
+ price = product.standard_price
taxes = account_tax.browse(cr, uid, map(lambda x: x.id, product.supplier_taxes_id))
fpos = fiscal_position_id and account_fiscal_position.browse(cr, uid, fiscal_position_id, context=context) or False
purchase_date = self._get_purchase_order_date(cr, uid, procurement, company, schedule_date, context=context)
#Passing partner_id to context for purchase order line integrity of Line name
- context.update({'lang': partner.lang, 'partner_id': partner_id})
+ new_context = context.copy()
+ new_context.update({'lang': partner.lang, 'partner_id': partner_id})
- product = prod_obj.browse(cr, uid, procurement.product_id.id, context=context)
+ product = prod_obj.browse(cr, uid, procurement.product_id.id, context=new_context)
taxes_ids = procurement.product_id.product_tmpl_id.supplier_taxes_id
taxes = acc_pos_obj.map_tax(cr, uid, partner.property_account_position, taxes_ids)
'fiscal_position': partner.property_account_position and partner.property_account_position.id or False,
'payment_term': partner.property_supplier_payment_term.id or False,
}
- res[procurement.id] = self.create_procurement_purchase_order(cr, uid, procurement, po_vals, line_vals, context=context)
+ res[procurement.id] = self.create_procurement_purchase_order(cr, uid, procurement, po_vals, line_vals, context=new_context)
self.write(cr, uid, [procurement.id], {'state': 'running', 'purchase_id': res[procurement.id]})
- self.running_send_note(cr, uid, [procurement.id], context=context)
+ self.purchase_order_create_note(cr, uid, ids, context=context)
return res
+
+ def _product_virtual_get(self, cr, uid, order_point):
+ procurement = order_point.procurement_id
+ if procurement and procurement.state != 'exception' and procurement.purchase_id and procurement.purchase_id.state in ('draft', 'confirmed'):
+ return None
+ return super(procurement_order, self)._product_virtual_get(cr, uid, order_point)
+
+ def purchase_order_create_note(self, cr, uid, ids, context=None):
+ for procurement in self.browse(cr, uid, ids, context=context):
+ body = _("Draft Purchase Order created")
+ self.message_post(cr, uid, [procurement.id], body=body, context=context)
procurement_order()
-class mail_message(osv.osv):
- _name = 'mail.message'
- _inherit = 'mail.message'
-
- def _postprocess_sent_message(self, cr, uid, message, context=None):
- if message.model == 'purchase.order':
+class mail_mail(osv.osv):
+ _name = 'mail.mail'
+ _inherit = 'mail.mail'
+
+ def _postprocess_sent_message(self, cr, uid, mail, context=None):
+ if mail.model == 'purchase.order':
+ wf_service = netsvc.LocalService("workflow")
+ wf_service.trg_validate(uid, 'purchase.order', mail.res_id, 'send_rfq', cr)
+ return super(mail_mail, self)._postprocess_sent_message(cr, uid, mail=mail, context=context)
+
+mail_mail()
+
+class product_template(osv.osv):
+ _name = 'product.template'
+ _inherit = 'product.template'
+ _columns = {
+ 'purchase_ok': fields.boolean('Can be Purchased', help="Specify if the product can be selected in a purchase order line."),
+ }
+ _defaults = {
+ 'purchase_ok': 1,
+ }
+
+product_template()
+
+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('default_model') == 'purchase.order' and context.get('default_res_id'):
wf_service = netsvc.LocalService("workflow")
- wf_service.trg_validate(uid, 'purchase.order', message.res_id, 'send_rfq', cr)
- return super(mail_message, self)._postprocess_sent_message(cr, uid, message=message, context=context)
+ wf_service.trg_validate(uid, 'purchase.order', context['default_res_id'], 'send_rfq', cr)
+ return super(mail_compose_message, self).send_mail(cr, uid, ids, context=context)
-mail_message()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: