##############################################################################
import time
+import pytz
+from openerp import SUPERUSER_ID
from datetime import datetime
from dateutil.relativedelta import relativedelta
from operator import attrgetter
]
_track = {
'state': {
- 'purchase.mt_rfq_confirmed': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'confirmed',
- 'purchase.mt_rfq_approved': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'approved',
- 'purchase.mt_rfq_done': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'done',
+ 'purchase.mt_rfq_confirmed': lambda self, cr, uid, obj, ctx=None: obj.state == 'confirmed',
+ 'purchase.mt_rfq_approved': lambda self, cr, uid, obj, ctx=None: obj.state == 'approved',
+ 'purchase.mt_rfq_done': lambda self, cr, uid, obj, ctx=None: obj.state == 'done',
},
}
_columns = {
'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."),
- 'currency_id': fields.related('pricelist_id', 'currency_id', type="many2one", relation="res.currency", string="Currency",readonly=True, required=True),
+ 'currency_id': fields.many2one('res.currency','Currency', readonly=True, required=True,states={'draft': [('readonly', False)],'sent': [('readonly', False)]}),
'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),
'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','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" \
+ help="Based on Purchase Order lines: place individual lines in 'Invoice Control / On Purchase Order 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."
+ "Based on incoming shipments: let you create an invoice when receptions are validated."
),
'minimum_planned_date':fields.function(_minimum_planned_date, fnct_inv=_set_minimum_planned_date, string='Expected Date', type='date', select=True, help="This is computed as the minimum scheduled date of all purchase order lines' products.",
store = {
'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,
+ 'currency_id': lambda self, cr, uid, context: self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id
}
_sql_constraints = [
('name_uniq', 'unique(name, company_id)', 'Order Reference must be unique per Company!'),
pick_ids += [picking.id for picking in po.picking_ids]
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)
+ action = self.pool[action_model].read(cr, uid, action_id, context=context)
ctx = eval(action['context'])
ctx.update({
'search_default_purchase_id': ids[0]
'default_composition_mode': 'comment',
})
return {
+ 'name': _('Compose Email'),
'type': 'ir.actions.act_window',
'view_type': 'form',
'view_mode': 'form',
self.write(cr, uid, [id], {'state' : 'confirmed', 'validator' : uid})
return True
+ def _choose_account_from_po_line(self, cr, uid, po_line, context=None):
+ fiscal_obj = self.pool.get('account.fiscal.position')
+ property_obj = self.pool.get('ir.property')
+ if po_line.product_id:
+ acc_id = po_line.product_id.property_account_expense.id
+ 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!'), _('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 = po_line.order_id.fiscal_position or False
+ return fiscal_obj.map_account(cr, uid, fpos, acc_id)
+
def _prepare_inv_line(self, cr, uid, account_id, order_line, context=None):
"""Collects require data from purchase order line that is used to create invoice line
for that purchase order line
'uos_id': order_line.product_uom.id or False,
'invoice_line_tax_id': [(6, 0, [x.id for x in order_line.taxes_id])],
'account_analytic_id': order_line.account_analytic_id.id or False,
+ 'purchase_line_id': order_line.id,
}
def action_cancel_draft(self, cr, uid, ids, context=None):
journal_obj = self.pool.get('account.journal')
inv_obj = self.pool.get('account.invoice')
inv_line_obj = self.pool.get('account.invoice.line')
- fiscal_obj = self.pool.get('account.fiscal.position')
- property_obj = self.pool.get('ir.property')
for order in self.browse(cr, uid, ids, context=context):
pay_acc_id = order.partner_id.property_account_payable.id
# generate invoice line correspond to PO line and link that to created invoice (inv_id) and PO line
inv_lines = []
for po_line in order.order_line:
- if po_line.product_id:
- acc_id = po_line.product_id.property_account_expense.id
- 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!'), _('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
- acc_id = fiscal_obj.map_account(cr, uid, fpos, acc_id)
-
+ acc_id = self._choose_account_from_po_line(cr, uid, po_line, context=context)
inv_line_data = self._prepare_inv_line(cr, uid, acc_id, po_line, context=context)
inv_line_id = inv_line_obj.create(cr, uid, inv_line_data, context=context)
inv_lines.append(inv_line_id)
'account_id': pay_acc_id,
'type': 'in_invoice',
'partner_id': order.partner_id.id,
- 'currency_id': order.pricelist_id.currency_id.id,
+ 'currency_id': order.currency_id.id,
'journal_id': len(journal_ids) and journal_ids[0] or False,
'invoice_line': [(6, 0, inv_lines)],
'origin': order.name,
self.signal_purchase_cancel(cr, uid, ids)
return True
+ def date_to_datetime(self, cr, uid, userdate, context=None):
+ """ Convert date values expressed in user's timezone to
+ server-side UTC timestamp, assuming a default arbitrary
+ time of 12:00 AM - because a time is needed.
+
+ :param str userdate: date string in in user time zone
+ :return: UTC datetime string for server-side use
+ """
+ # TODO: move to fields.datetime in server after 7.0
+ user_date = datetime.strptime(userdate, DEFAULT_SERVER_DATE_FORMAT)
+ if context and context.get('tz'):
+ tz_name = context['tz']
+ else:
+ tz_name = self.pool.get('res.users').read(cr, SUPERUSER_ID, uid, ['tz'])['tz']
+ if tz_name:
+ utc = pytz.timezone('UTC')
+ context_tz = pytz.timezone(tz_name)
+ user_datetime = user_date + relativedelta(hours=12.0)
+ local_timestamp = context_tz.localize(user_datetime, is_dst=False)
+ user_datetime = local_timestamp.astimezone(utc)
+ return user_datetime.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
+ return user_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
+
def _prepare_order_picking(self, cr, uid, order, context=None):
return {
'name': self.pool.get('ir.sequence').get(cr, uid, 'stock.picking.in'),
'origin': order.name + ((order.origin and (':' + order.origin)) or ''),
- 'date': order.date_order,
+ 'date': self.date_to_datetime(cr, uid, order.date_order, context),
'partner_id': order.dest_address_id.id or order.partner_id.id,
'invoice_state': '2binvoiced' if order.invoice_method == 'picking' else 'none',
'type': 'in',
}
def _prepare_order_line_move(self, cr, uid, order, order_line, picking_id, context=None):
+ ''' prepare the stock move data from the PO line '''
return {
'name': order_line.name or '',
'product_id': order_line.product_id.id,
'product_uos_qty': order_line.product_qty,
'product_uom': order_line.product_uom.id,
'product_uos': order_line.product_uom.id,
- 'date': order_line.date_planned,
- 'date_expected': order_line.date_planned,
+ 'date': self.date_to_datetime(cr, uid, order.date_order, context),
+ 'date_expected': self.date_to_datetime(cr, uid, order_line.date_planned, context),
'location_id': order.partner_id.property_stock_supplier.id,
'location_dest_id': order.location_id.id,
'picking_id': picking_id,
'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),
- 'date_order': fields.related('order_id','date_order',string='Order Date',readonly=True,type="date")
+ 'date_order': fields.related('order_id','date_order',string='Order Date',readonly=True,type="date"),
}
_defaults = {
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
+ #call name_get() with partner in the context to eventually match name and description in the seller_ids field
+ dummy, name = product_product.name_get(cr, uid, product_id, context=context_partner)[0]
if product.description_purchase:
name += '\n' + product.description_purchase
res['value'].update({'name': name})
self.write(cr, uid, ids, {'state': 'confirmed'}, context=context)
return True
-purchase_order_line()
class procurement_order(osv.osv):
_inherit = 'procurement.order'
partner_obj = self.pool.get('res.partner')
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
for procurement in self.browse(cr, uid, ids, context=context):
- if not procurement.product_id.seller_ids:
- message = _('No supplier defined for this product !')
- self.message_post(cr, uid, [procurement.id], body=message)
- cr.execute('update procurement_order set message=%s where id=%s', (message, procurement.id))
- return False
+ message = ''
partner = procurement.product_id.seller_id #Taken Main Supplier of Product of Procurement.
- if not partner:
+ if not procurement.product_id.seller_ids:
+ message = _('No supplier defined for this product !')
+ elif not partner:
message = _('No default supplier defined for this product')
- self.message_post(cr, uid, [procurement.id], body=message)
- cr.execute('update procurement_order set message=%s where id=%s', (message, procurement.id))
+ elif not partner_obj.address_get(cr, uid, [partner.id], ['delivery'])['delivery']:
+ message = _('No address defined for the supplier')
+
+ if message:
+ if procurement.message != message:
+ cr.execute('update procurement_order set message=%s where id=%s', (message, procurement.id))
return False
+
if user.company_id and user.company_id.partner_id:
if partner.id == user.company_id.partner_id.id:
raise osv.except_osv(_('Configuration Error!'), _('The product "%s" has been defined with your company as reseller which seems to be a configuration error!' % procurement.product_id.name))
- address_id = partner_obj.address_get(cr, uid, [partner.id], ['delivery'])['delivery']
- if not address_id:
- message = _('No address defined for the supplier')
- self.message_post(cr, uid, [procurement.id], body=message)
- cr.execute('update procurement_order set message=%s where id=%s', (message, procurement.id))
- return False
return True
res = super(account_invoice, self).invoice_validate(cr, uid, ids, context=context)
purchase_order_obj = self.pool.get('purchase.order')
po_ids = purchase_order_obj.search(cr, uid, [('invoice_ids', 'in', ids)], context=context)
- if po_ids:
- purchase_order_obj.message_post(cr, uid, po_ids, body=_("Invoice received"), context=context)
+ for po_id in po_ids:
+ purchase_order_obj.message_post(cr, uid, po_id, body=_("Invoice received"), context=context)
return res
def confirm_paid(self, cr, uid, ids, context=None):
purchase_order_obj.message_post(cr, uid, po_ids, body=_("Invoice paid"), context=context)
return res
+class account_invoice_line(osv.Model):
+ """ Override account_invoice_line to add the link to the purchase order line it is related to"""
+ _inherit = 'account.invoice.line'
+ _columns = {
+ 'purchase_line_id': fields.many2one('purchase.order.line',
+ 'Purchase Order Line', ondelete='set null', select=True,
+ readonly=True),
+ }
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: