#
##############################################################################
-from mx import DateTime
import time
from datetime import datetime
from dateutil.relativedelta import relativedelta
from osv import osv, fields
import netsvc
-import ir
import pooler
-from tools import config
from tools.translate import _
import decimal_precision as dp
from osv.orm import browse_record, browse_null
# Model definition
#
class purchase_order(osv.osv):
+
def _calc_amount(self, cr, uid, ids, prop, unknow_none, unknow_dict):
res = {}
for order in self.browse(cr, uid, ids):
def _amount_all(self, cr, uid, ids, field_name, arg, context=None):
res = {}
cur_obj=self.pool.get('res.currency')
- for order in self.browse(cr, uid, ids):
+ for order in self.browse(cr, uid, ids, context=context):
res[order.id] = {
'amount_untaxed': 0.0,
'amount_tax': 0.0,
'amount_total': 0.0,
- }
+ }
val = val1 = 0.0
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, order.partner_address_id.id, line.product_id.id, order.partner_id)['taxes']:
- val += c['amount']
+ 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)
res[order.id]['amount_total']=res[order.id]['amount_untaxed'] + res[order.id]['amount_tax']
if not value: return False
if type(ids)!=type([]):
ids=[ids]
- for po in self.browse(cr, uid, ids, context):
- cr.execute("""update purchase_order_line set
- date_planned=%s
- where
- order_id=%s and
- (date_planned=%s or date_planned<%s)""", (value,po.id,po.minimum_planned_date,value))
+ for po in self.browse(cr, uid, ids, context=context):
+ if po.order_line:
+ cr.execute("""update purchase_order_line set
+ date_planned=%s
+ where
+ order_id=%s and
+ (date_planned=%s or date_planned<%s)""", (value,po.id,po.minimum_planned_date,value))
+ cr.execute("""update purchase_order set
+ minimum_planned_date=%s where id=%s""", (value, po.id))
return True
def _minimum_planned_date(self, cr, uid, ids, field_name, arg, context=None):
res={}
purchase_obj=self.browse(cr, uid, ids, context=context)
for purchase in purchase_obj:
- res[purchase.id] = time.strftime('%Y-%m-%d %H:%M:%S')
+ res[purchase.id] = False
if purchase.order_line:
min_date=purchase.order_line[0].date_planned
for line in purchase.order_line:
res[purchase.id]=min_date
return res
+
def _invoiced_rate(self, cursor, user, ids, name, arg, context=None):
res = {}
for purchase in self.browse(cursor, user, ids, context=context):
tot = 0.0
- if purchase.invoice_id and purchase.invoice_id.state not in ('draft','cancel'):
- tot += purchase.invoice_id.amount_untaxed
+ for invoice in purchase.invoice_ids:
+ if invoice.state not in ('draft','cancel'):
+ tot += invoice.amount_untaxed
+
if purchase.amount_untaxed:
- res[purchase.id] = tot * 100.0 / purchase.amount_untaxed
+ res[purchase.id] = min(100.0, tot * 100.0 / (purchase.amount_untaxed))
else:
res[purchase.id] = 0.0
return res
def _invoiced(self, cursor, user, ids, name, arg, context=None):
res = {}
for purchase in self.browse(cursor, user, ids, context=context):
- if purchase.invoice_id.reconciled:
- res[purchase.id] = purchase.invoice_id.reconciled
- else:
- res[purchase.id] = False
+ invoiced = False
+ if purchase.invoiced_rate == 100.00:
+ invoiced = True
+ res[purchase.id] = invoiced
return res
+ STATE_SELECTION = [
+ ('draft', 'Request for Quotation'),
+ ('wait', 'Waiting'),
+ ('confirmed', 'Waiting Approval'),
+ ('approved', 'Approved'),
+ ('except_picking', 'Shipping Exception'),
+ ('except_invoice', 'Invoice Exception'),
+ ('done', 'Done'),
+ ('cancel', 'Cancelled')
+ ]
+
_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."
),
'partner_ref': fields.char('Supplier Reference', states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]}, size=64),
- 'date_order':fields.date('Date Ordered', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)]}, help="Date on which this document has been created."),
- 'date_approve':fields.date('Date Approved', readonly=1, help="Date on which purchase order has been approved"),
+ 'date_order':fields.date('Date Ordered', 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),
'partner_address_id':fields.many2one('res.partner.address', 'Address', required=True,
states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]},domain="[('partner_id', '=', partner_id)]"),
'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]}),
'location_id': fields.many2one('stock.location', 'Destination', required=True, domain=[('usage','<>','view')]),
'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([('draft', 'Request for Quotation'), ('wait', 'Waiting'), ('confirmed', 'Waiting Supplier Ack'), ('approved', 'Approved'),('except_picking', 'Shipping Exception'), ('except_invoice', 'Invoice Exception'), ('done', 'Done'), ('cancel', 'Cancelled')], 'State', 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),
+ 'state': fields.selection(STATE_SELECTION, 'State', 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),
'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('Notes'),
- 'invoice_id': fields.many2one('account.invoice', 'Invoice', readonly=True, help="An invoice generated for a purchase order"),
+ 'invoice_ids': fields.many2many('account.invoice', 'purchase_invoice_rel', 'purchase_id', 'invoice_id', 'Invoices', help="Invoices generated for a purchase order"),
'picking_ids': fields.one2many('stock.picking', 'purchase_id', 'Picking List', readonly=True, help="This is the list of picking list that have been generated for this purchase"),
'shipped':fields.boolean('Received', readonly=True, select=True, help="It indicates that a picking has been done"),
'shipped_rate': fields.function(_shipped_rate, method=True, string='Received', type='float'),
help="From Order: a draft invoice will be pre-generated based on the purchase order. The accountant " \
"will just have to validate this invoice for control.\n" \
"From Picking: a draft invoice will be pre-generated based on validated receptions.\n" \
- "Manual: no invoice will be pre-generated. The accountant will have to encode manually."
+ "Manual: allows you to generate suppliers invoices by chosing in the uninvoiced lines of all manual purchase orders."
+ ),
+ 'minimum_planned_date':fields.function(_minimum_planned_date, fnct_inv=_set_minimum_planned_date, method=True, string='Expected Date', type='date', select=True, help="This is computed as the minimum scheduled date of all purchase order lines' products.",
+ store = {
+ 'purchase.order.line': (_get_order, ['date_planned'], 10),
+ }
),
- 'minimum_planned_date':fields.function(_minimum_planned_date, fnct_inv=_set_minimum_planned_date, method=True,store=True, string='Expected Date', type='date', help="This is computed as the minimum scheduled date of all purchase order lines' products."),
'amount_untaxed': fields.function(_amount_all, method=True, digits_compute= dp.get_precision('Purchase Price'), string='Untaxed Amount',
store={
'purchase.order.line': (_get_order, None, 10),
'company_id': fields.many2one('res.company','Company',required=True,select=1),
}
_defaults = {
- 'date_order': time.strftime('%Y-%m-%d'),
+ 'date_order': lambda *a: time.strftime('%Y-%m-%d'),
'state': 'draft',
'name': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'purchase.order'),
'shipped': 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),
}
+ _sql_constraints = [
+ ('name_uniq', 'unique(name)', 'Order Reference must be unique !'),
+ ]
_name = "purchase.order"
_description = "Purchase Order"
_order = "name desc"
def unlink(self, cr, uid, ids, context=None):
- purchase_orders = self.read(cr, uid, ids, ['state'])
+ purchase_orders = self.read(cr, uid, ids, ['state'], context=context)
unlink_ids = []
for s in purchase_orders:
if s['state'] in ['draft','cancel']:
unlink_ids.append(s['id'])
else:
- raise osv.except_osv(_('Invalid action !'), _('Cannot delete Purchase Order(s) which are in %s State!' % s['state']))
+ raise osv.except_osv(_('Invalid action !'), _('Cannot delete Purchase Order(s) which are in %s State!') % _(dict(purchase_order.STATE_SELECTION).get(s['state'])))
# TODO: temporary fix in 5.0, to remove in 5.2 when subflows support
# automatically sending subflow.delete upon deletion
return super(purchase_order, self).unlink(cr, uid, unlink_ids, context=context)
- def button_dummy(self, cr, uid, ids, context={}):
+ def button_dummy(self, cr, uid, ids, context=None):
return True
def onchange_dest_address_id(self, cr, uid, ids, adr_id):
if not adr_id:
return {}
- part_id = self.pool.get('res.partner.address').read(cr, uid, [adr_id], ['partner_id'])[0]['partner_id'][0]
- loc_id = self.pool.get('res.partner').browse(cr, uid, part_id).property_stock_customer.id
- return {'value':{'location_id': loc_id, 'warehouse_id': False}}
+ values = {'warehouse_id': False}
+ part_id = self.pool.get('res.partner.address').browse(cr, uid, adr_id).partner_id
+ if part_id:
+ loc_id = part_id.property_stock_customer.id
+ values.update({'location_id': loc_id})
+ return {'value':values}
def onchange_warehouse_id(self, cr, uid, ids, warehouse_id):
if not warehouse_id:
fiscal_position = part.property_account_position and part.property_account_position.id or False
return {'value':{'partner_address_id': addr['default'], 'pricelist_id': pricelist, 'fiscal_position': fiscal_position}}
- def wkf_approve_order(self, cr, uid, ids, context={}):
+ def wkf_approve_order(self, cr, uid, ids, context=None):
self.write(cr, uid, ids, {'state': 'approved', 'date_approve': time.strftime('%Y-%m-%d')})
- for (id,name) in self.name_get(cr, uid, ids):
- message = _('Purchase order ') + " '" + name + "' "+_("is approved by the supplier")
- self.log(cr, uid, id, message)
return True
#TODO: implement messages system
- def wkf_confirm_order(self, cr, uid, ids, context={}):
- product = []
+ def wkf_confirm_order(self, cr, uid, ids, context=None):
todo = []
- for po in self.browse(cr, uid, ids):
+ for po in self.browse(cr, uid, ids, context=context):
if not po.order_line:
raise osv.except_osv(_('Error !'),_('You can not confirm purchase order without Purchase Order Lines.'))
for line in po.order_line:
if line.state=='draft':
todo.append(line.id)
- current_name = self.name_get(cr, uid, ids)[0][1]
+ message = _("Purchase order '%s' is confirmed.") % (po.name,)
+ self.log(cr, uid, po.id, message)
+# 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})
- for line in po.order_line:
- product.append(line.product_id.default_code or '')
- params = ', '.join(map(lambda x : str(x), product))
- message = _('Purchase order ') + " '" + po.name + "' "+_('placed on')+ " '" + po.date_order + "' "+_('for')+" '" + params + "' "+ _("is confirmed")
- self.log(cr, uid, id, message)
return True
def wkf_warn_buyer(self, cr, uid, ids):
if manager and not (manager.id in managers):
managers.append(manager.id)
for manager_id in managers:
- request.create(cr, uid,
- {'name' : "Purchase amount over the limit",
+ request.create(cr, uid,{
+ 'name' : _("Purchase amount over the limit"),
'act_from' : uid,
'act_to' : manager_id,
- 'body': 'Somebody has just confirmed a purchase with an amount over the defined limit',
+ 'body': _('Somebody has just confirmed a purchase with an amount over the defined limit'),
'ref_partner_id': po.partner_id.id,
'ref_doc1': 'purchase.order,%d' % (po.id,),
- })
+ })
def inv_line_create(self, cr, uid, a, ol):
return (0, False, {
'name': ol.name,
wf_service.trg_delete(uid, 'purchase.order', p_id, cr)
wf_service.trg_create(uid, 'purchase.order', p_id, cr)
for (id,name) in self.name_get(cr, uid, ids):
- message = _('Purchase order') + " '" + name + "' "+ _("is in the draft state")
- self.log(cr, uid, id, message)
+ message = _("Purchase order '%s' has been set in draft state.") % name
+ self.log(cr, uid, id, message)
return True
def action_invoice_create(self, cr, uid, ids, *args):
res = False
-
+ property_obj = self.pool.get('ir.property')
+ fp_obj = self.pool.get('account.fiscal.position')
journal_obj = self.pool.get('account.journal')
for o in self.browse(cr, uid, ids):
il = []
for ol in o.order_line:
todo.append(ol.id)
if ol.product_id:
- a = ol.product_id.product_tmpl_id.property_account_expense.id
- if not a:
- a = ol.product_id.categ_id.property_account_expense_categ.id
- if not a:
+ acc_id = ol.product_id.product_tmpl_id.property_account_expense.id
+ if not acc_id:
+ acc_id = ol.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)') % (ol.product_id.name, ol.product_id.id,))
else:
- a = self.pool.get('ir.property').get(cr, uid, 'property_account_expense_categ', 'product.category').id
+ prop = property_obj.get(cr, uid, 'property_account_expense_categ', 'product.category')
+ acc_id = prop and prop.id or False
fpos = o.fiscal_position or False
- a = self.pool.get('account.fiscal.position').map_account(cr, uid, fpos, a)
- il.append(self.inv_line_create(cr, uid, a, ol))
+ acc_id = fp_obj.map_account(cr, uid, fpos, acc_id)
+ if not acc_id:
+ raise osv.except_osv(_('Error !'),
+ _('There is no expense account defined in default Properties for Product Category or Fiscal Position is not defined !'))
+ il.append(self.inv_line_create(cr, uid, acc_id, ol))
- a = o.partner_id.property_account_payable.id
+ acc_id = o.partner_id.property_account_payable.id
journal_ids = journal_obj.search(cr, uid, [('type', '=','purchase'),('company_id', '=', o.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)') % (o.company_id.name, o.company_id.id))
inv = {
'name': o.partner_ref or o.name,
- 'reference': "P%dPO%d" % (o.partner_id.id, o.id),
- 'account_id': a,
+ 'reference': o.partner_ref or o.name,
+ 'account_id': acc_id,
'type': 'in_invoice',
'partner_id': o.partner_id.id,
'currency_id': o.pricelist_id.currency_id.id,
'journal_id': len(journal_ids) and journal_ids[0] or False,
'origin': o.name,
'invoice_line': il,
- 'fiscal_position': o.partner_id.property_account_position.id,
+ 'fiscal_position': o.fiscal_position.id or o.partner_id.property_account_position.id,
'payment_term': o.partner_id.property_payment_term and o.partner_id.property_payment_term.id or False,
'company_id': o.company_id.id,
}
- inv_id = self.pool.get('account.invoice').create(cr, uid, inv, {'type':'in_invoice'})
+ inv_id = self.pool.get('account.invoice').create(cr, uid, inv, {'type':'in_invoice', 'journal_type': 'purchase'})
self.pool.get('account.invoice').button_compute(cr, uid, [inv_id], {'type':'in_invoice'}, set_total=True)
self.pool.get('purchase.order.line').write(cr, uid, todo, {'invoiced':True})
- self.write(cr, uid, [o.id], {'invoice_id': inv_id})
+ self.write(cr, uid, [o.id], {'invoice_ids': [(4, inv_id)]})
res = inv_id
return res
return True
return False
- def action_cancel(self, cr, uid, ids, context={}):
- ok = True
- purchase_order_line_obj = self.pool.get('purchase.order.line')
- for purchase in self.browse(cr, uid, ids):
+ def action_cancel(self, cr, uid, ids, context=None):
+ for purchase in self.browse(cr, uid, ids, context=context):
for pick in purchase.picking_ids:
if pick.state not in ('draft','cancel'):
raise osv.except_osv(
for pick in purchase.picking_ids:
wf_service = netsvc.LocalService("workflow")
wf_service.trg_validate(uid, 'stock.picking', pick.id, 'button_cancel', cr)
- inv = purchase.invoice_id
- if inv and inv.state not in ('cancel','draft'):
- raise osv.except_osv(
- _('Could not cancel this purchase order !'),
- _('You must first cancel all invoices attached to this purchase order.'))
- if inv:
- wf_service = netsvc.LocalService("workflow")
- wf_service.trg_validate(uid, 'account.invoice', inv.id, 'invoice_cancel', cr)
+ for inv in purchase.invoice_ids:
+ if inv and inv.state not in ('cancel','draft'):
+ raise osv.except_osv(
+ _('Could not cancel this purchase order !'),
+ _('You must first cancel all invoices attached to this purchase order.'))
+ if inv:
+ wf_service = netsvc.LocalService("workflow")
+ wf_service.trg_validate(uid, 'account.invoice', inv.id, 'invoice_cancel', cr)
self.write(cr,uid,ids,{'state':'cancel'})
- message = _('Purchase order ') + " '" + purchase.name + "' "+ _("is cancelled")
- self.log(cr, uid, id, message)
+ for (id,name) in self.name_get(cr, uid, ids):
+ message = _("Purchase order '%s' is cancelled.") % name
+ self.log(cr, uid, id, message)
return True
def action_picking_create(self,cr, uid, ids, *args):
picking_id = False
for order in self.browse(cr, uid, ids):
+ reception_address_id = False
+ if order.dest_address_id:
+ reception_address_id = order.dest_address_id.id
+ elif order.warehouse_id and order.warehouse_id.partner_address_id:
+ reception_address_id = order.warehouse_id.partner_address_id.id
+ else:
+ if order.company_id.partner_id.address:
+ addresses_default = [address.id for address in order.company_id.partner_id.address if address.type == 'default']
+ addresses_delivery = [address.id for address in order.company_id.partner_id.address if address.type == 'delivery']
+ reception_address_id = (addresses_delivery and addresses_delivery[0]) or (addresses_default and addresses_default[0]) or False
loc_id = order.partner_id.property_stock_supplier.id
istate = 'none'
if order.invoice_method=='picking':
'name': pick_name,
'origin': order.name+((order.origin and (':'+order.origin)) or ''),
'type': 'in',
- 'address_id': order.dest_address_id.id or order.partner_address_id.id,
+ 'address_id': reception_address_id,
'invoice_state': istate,
'purchase_id': order.id,
'company_id': order.company_id.id,
if order_line.product_id.product_tmpl_id.type in ('product', 'consu'):
dest = order.location_id.id
move = self.pool.get('stock.move').create(cr, uid, {
- 'name': 'PO:'+order_line.name,
+ 'name': order.name + ': ' +(order_line.name or ''),
'product_id': order_line.product_id.id,
'product_qty': order_line.product_qty,
'product_uos_qty': order_line.product_qty,
'product_uom': order_line.product_uom.id,
'product_uos': order_line.product_uom.id,
- 'date_planned': order_line.date_planned,
+ 'date': order_line.date_planned,
'date_expected': order_line.date_planned,
'location_id': loc_id,
'location_dest_id': dest,
'state': 'draft',
'purchase_line_id': order_line.id,
'company_id': order.company_id.id,
+ 'price_unit': order_line.price_unit
})
if order_line.move_dest_id:
self.pool.get('stock.move').write(cr, uid, [order_line.move_dest_id.id], {'location_id':order.location_id.id})
wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr)
return picking_id
- def copy(self, cr, uid, id, default=None,context={}):
+ def copy(self, cr, uid, id, default=None, context=None):
if not default:
default = {}
default.update({
'state':'draft',
'shipped':False,
'invoiced':False,
- 'invoice_id':False,
- 'picking_ids':[],
+ 'invoice_ids': [],
+ 'picking_ids': [],
'name': self.pool.get('ir.sequence').get(cr, uid, 'purchase.order'),
})
return super(purchase_order, self).copy(cr, uid, id, default, context)
- def do_merge(self, cr, uid, ids, context):
+ def do_merge(self, cr, uid, ids, context=None):
"""
To merge similar type of purchase orders.
Orders will only be merged if:
new_orders = {}
- for porder in [order for order in self.browse(cr, uid, ids) if order.state == 'draft']:
+ for porder in [order for order in self.browse(cr, uid, ids, context=context) if order.state == 'draft']:
order_key = make_key(porder, ('partner_id', 'location_id', 'pricelist_id'))
new_order = new_orders.setdefault(order_key, ({}, []))
new_order[1].append(porder.id)
order_infos = new_order[0]
if not order_infos:
order_infos.update({
- 'origin': porder.origin,
- 'date_order': time.strftime('%Y-%m-%d'),
- 'partner_id': porder.partner_id.id,
- 'partner_address_id': porder.partner_address_id.id,
- 'dest_address_id': porder.dest_address_id.id,
- 'warehouse_id': porder.warehouse_id.id,
- 'location_id': porder.location_id.id,
- 'pricelist_id': porder.pricelist_id.id,
- 'state': 'draft',
- 'order_line': {},
- 'notes': '%s' % (porder.notes or '',),
- 'fiscal_position': porder.fiscal_position and porder.fiscal_position.id or False,
+ 'origin': porder.origin,
+ 'date_order': time.strftime('%Y-%m-%d'),
+ 'partner_id': porder.partner_id.id,
+ 'partner_address_id': porder.partner_address_id.id,
+ 'dest_address_id': porder.dest_address_id.id,
+ 'warehouse_id': porder.warehouse_id.id,
+ 'location_id': porder.location_id.id,
+ 'pricelist_id': porder.pricelist_id.id,
+ 'state': 'draft',
+ 'order_line': {},
+ 'notes': '%s' % (porder.notes or '',),
+ 'fiscal_position': porder.fiscal_position and porder.fiscal_position.id or False,
})
else:
- #order_infos['name'] += ', %s' % porder.name
if porder.notes:
order_infos['notes'] = (order_infos['notes'] or '') + ('\n%s' % (porder.notes,))
if porder.origin:
allorders = []
+ orders_info = {}
for order_key, (order_data, old_ids) in new_orders.iteritems():
# skip merges with only one order
if len(old_ids) < 2:
# create the new order
neworder_id = self.create(cr, uid, order_data)
+ orders_info.update({neworder_id: old_ids})
allorders.append(neworder_id)
# make triggers pointing to the old orders point to the new order
for old_id in old_ids:
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 allorders
+ return orders_info
purchase_order()
class purchase_order_line(osv.osv):
- def _amount_line(self, cr, uid, ids, prop, arg,context):
+ def _amount_line(self, cr, uid, ids, prop, arg, context=None):
res = {}
cur_obj=self.pool.get('res.currency')
tax_obj = self.pool.get('account.tax')
_columns = {
'name': fields.char('Description', size=256, required=True),
'product_qty': fields.float('Quantity', required=True, digits=(16,2)),
- 'date_planned': fields.date('Scheduled date', required=True),
+ 'date_planned': fields.date('Scheduled Date', required=True, select=True),
'taxes_id': fields.many2many('account.tax', 'purchase_order_taxe', 'ord_id', 'tax_id', 'Taxes'),
'product_uom': fields.many2one('product.uom', 'Product UOM', required=True),
'product_id': fields.many2one('product.product', 'Product', domain=[('purchase_ok','=',True)], change_default=True),
'notes': fields.text('Notes'),
'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'),
+ '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')], 'State', 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 \'Cancelled\' state 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"),
+ '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")
}
_name = 'purchase.order.line'
_description = 'Purchase Order Line'
- def copy_data(self, cr, uid, id, default=None,context={}):
+ def copy_data(self, cr, uid, id, default=None, context=None):
if not default:
default = {}
- default.update({'state':'draft', 'move_ids':[],'invoiced':0,'invoice_lines':[]})
+ default.update({'state':'draft', 'move_ids':[], 'move_dest_id':False, 'invoiced':0,'invoice_lines':[]})
return super(purchase_order_line, self).copy_data(cr, uid, id, default, context)
def product_id_change(self, cr, uid, ids, pricelist, product, qty, uom,
if not product:
return {'value': {'price_unit': price_unit or 0.0, 'name': name or '',
'notes': notes or'', 'product_uom' : uom or False}, 'domain':{'product_uom':[]}}
+ res = {}
prod= self.pool.get('product.product').browse(cr, uid, product)
+
+ product_uom_pool = self.pool.get('product.uom')
lang=False
if partner_id:
lang=self.pool.get('res.partner').read(cr, uid, partner_id, ['lang'])['lang']
- context={'lang':lang}
- context['partner_id'] = partner_id
-
+ context = self.pool.get('res.users').context_get(cr, uid)
+ context_partner = {'lang':lang, 'partner_id': partner_id}
prod = self.pool.get('product.product').browse(cr, uid, product, context=context)
prod_uom_po = prod.uom_po_id.id
if not uom:
date_order = time.strftime('%Y-%m-%d')
qty = qty or 1.0
seller_delay = 0
+
+ prod_name = self.pool.get('product.product').name_get(cr, uid, [prod.id], context=context_partner)[0][1]
+ res = {}
for s in prod.seller_ids:
if s.name.id == partner_id:
seller_delay = s.delay
- temp_qty = s.qty # supplier _qty assigned to temp
+ if s.product_uom:
+ temp_qty = product_uom_pool._compute_qty(cr, uid, s.product_uom.id, s.min_qty, to_uom_id=prod.uom_id.id)
+ uom = s.product_uom.id #prod_uom_po
+ temp_qty = s.min_qty # supplier _qty assigned to temp
if qty < temp_qty: # If the supplier quantity is greater than entered from user, set minimal.
qty = temp_qty
- if price_unit:
- price = price_unit
- else:
- price = self.pool.get('product.pricelist').price_get(cr,uid,[pricelist],
- product, qty or 1.0, partner_id, {
+ res.update({'warning': {'title': _('Warning'), 'message': _('The selected supplier has a minimal quantity set to %s, you cannot purchase less.') % qty}})
+ qty_in_product_uom = product_uom_pool._compute_qty(cr, uid, uom, qty, to_uom_id=prod.uom_id.id)
+ price = self.pool.get('product.pricelist').price_get(cr,uid,[pricelist],
+ product, qty_in_product_uom or 1.0, partner_id, {
'uom': uom,
'date': date_order,
})[pricelist]
+ if price is False:
+ warning = {
+ 'title': 'No valid pricelist line found !',
+ 'message':
+ "Couldn't find a pricelist line matching this product and quantity.\n"
+ "You have to change either the product, the quantity or the pricelist."
+ }
+ res.update({'warning': warning})
dt = (datetime.now() + relativedelta(days=int(seller_delay) or 0.0)).strftime('%Y-%m-%d %H:%M:%S')
- prod_name = self.pool.get('product.product').name_get(cr, uid, [prod.id])[0][1]
- res = {'value': {'price_unit': price, 'name': name or prod_name,
+ res.update({'value': {'price_unit': price, 'name': prod_name,
'taxes_id':map(lambda x: x.id, prod.supplier_taxes_id),
'date_planned': date_planned or dt,'notes': notes or prod.description_purchase,
'product_qty': qty,
- 'product_uom': uom}}
+ 'product_uom': uom}})
domain = {}
- partner = self.pool.get('res.partner').browse(cr, uid, partner_id)
taxes = self.pool.get('account.tax').browse(cr, uid,map(lambda x: x.id, prod.supplier_taxes_id))
fpos = fiscal_position and self.pool.get('account.fiscal.position').browse(cr, uid, fiscal_position) or False
res['value']['taxes_id'] = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos, taxes)
return res
def product_uom_change(self, cr, uid, ids, pricelist, product, qty, uom,
- partner_id, date_order=False,fiscal_position=False):
+ partner_id, date_order=False, fiscal_position=False, date_planned=False,
+ name=False, price_unit=False, notes=False):
res = self.product_id_change(cr, uid, ids, pricelist, product, qty, uom,
- partner_id, date_order=date_order,fiscal_position=fiscal_position)
+ partner_id, date_order=date_order, fiscal_position=fiscal_position, date_planned=date_planned,
+ name=name, price_unit=price_unit, notes=notes)
if 'product_uom' in res['value']:
+ if uom and (uom != res['value']['product_uom']) and res['value']['product_uom']:
+ seller_uom_name = self.pool.get('product.uom').read(cr, uid, [res['value']['product_uom']], ['name'])[0]['name']
+ res.update({'warning': {'title': _('Warning'), 'message': _('The selected supplier only sells this product by %s') % seller_uom_name }})
del res['value']['product_uom']
if not uom:
res['value']['price_unit'] = 0.0
return res
- def action_confirm(self, cr, uid, ids, context={}):
- self.write(cr, uid, ids, {'state': 'confirmed'}, context)
- for (id,name) in self.name_get(cr, uid, ids):
- message = _('Purchase order line') + " '" + name + "' "+ _("is confirmed")
- self.log(cr, uid, id, message)
+ def action_confirm(self, cr, uid, ids, context=None):
+ self.write(cr, uid, ids, {'state': 'confirmed'}, context=context)
return True
purchase_order_line()
'purchase_id': fields.many2one('purchase.order', 'Purchase Order'),
}
- def action_po_assign(self, cr, uid, ids, context={}):
+ def action_po_assign(self, cr, uid, ids, context=None):
""" This is action which call from workflow to assign purchase order to procurements
@return: True
"""
res = res.values()
return len(res) and res[0] or 0 #TO CHECK: why workflow is generated error if return not integer value
- def make_po(self, cr, uid, ids, context={}):
+ def make_po(self, cr, uid, ids, context=None):
""" Make purchase order from procurement
@return: New created Purchase Orders procurement wise
"""
res = {}
- company = self.pool.get('res.users').browse(cr, uid, uid, context).company_id
+ if context is None:
+ context = {}
+ company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id
partner_obj = self.pool.get('res.partner')
uom_obj = self.pool.get('product.uom')
pricelist_obj = self.pool.get('product.pricelist')
prod_obj = self.pool.get('product.product')
acc_pos_obj = self.pool.get('account.fiscal.position')
po_obj = self.pool.get('purchase.order')
- for procurement in self.browse(cr, uid, ids):
+ po_line_obj = self.pool.get('purchase.order.line')
+ for procurement in self.browse(cr, uid, ids, context=context):
res_id = procurement.move_id.id
partner = procurement.product_id.seller_id # Taken Main Supplier of Product of Procurement.
seller_qty = procurement.product_id.seller_qty
partner_id = partner.id
address_id = partner_obj.address_get(cr, uid, [partner_id], ['delivery'])['delivery']
pricelist_id = partner.property_product_pricelist_purchase.id
+ fiscal_position = partner.property_account_position and partner.property_account_position.id or False
uom_id = procurement.product_id.uom_po_id.id
if seller_qty:
qty = max(qty,seller_qty)
- price = pricelist_obj.price_get(cr, uid, [pricelist_id], procurement.product_id.id, qty, False, {'uom': uom_id})[pricelist_id]
+ price = pricelist_obj.price_get(cr, uid, [pricelist_id], procurement.product_id.id, qty, partner_id, {'uom': uom_id})[pricelist_id]
- newdate = DateTime.strptime(procurement.date_planned, '%Y-%m-%d %H:%M:%S')
- newdate = newdate - DateTime.RelativeDateTime(days=company.po_lead)
- newdate = newdate - seller_delay
+ newdate = datetime.strptime(procurement.date_planned, '%Y-%m-%d %H:%M:%S')
+ newdate = (newdate - relativedelta(days=company.po_lead)) - relativedelta(days=seller_delay)
+
+ res_onchange = po_line_obj.product_id_change(cr, uid, ids, pricelist_id, procurement.product_id.id, qty, uom_id,
+ partner_id, time.strftime('%Y-%m-%d'), fiscal_position=fiscal_position, date_planned=datetime.now() + relativedelta(days=seller_delay or 0.0),
+ name=procurement.name, price_unit=procurement.product_id.list_price, notes=procurement.product_id.description_purchase)
#Passing partner_id to context for purchase order line integrity of Line name
context.update({'lang': partner.lang, 'partner_id': partner_id})
line = {
'name': product.partner_ref,
- 'product_qty': qty,
+ 'product_qty': res_onchange['value']['product_qty'],
'product_id': procurement.product_id.id,
- 'product_uom': uom_id,
- 'price_unit': price,
+ 'product_uom': res_onchange['value']['product_uom'],
+ 'price_unit': res_onchange['value']['price_unit'],
'date_planned': newdate.strftime('%Y-%m-%d %H:%M:%S'),
'move_dest_id': res_id,
'notes': product.description_purchase,
procurement_order()
+class stock_invoice_onshipping(osv.osv_memory):
+ _inherit = "stock.invoice.onshipping"
+
+ def create_invoice(self, cr, uid, ids, context=None):
+ if context is None:
+ context = {}
+ res = super(stock_invoice_onshipping,self).create_invoice(cr, uid, ids, context=context)
+ purchase_obj = self.pool.get('purchase.order')
+ picking_obj = self.pool.get('stock.picking')
+ for pick_id in res:
+ pick = picking_obj.browse(cr, uid, pick_id, context=context)
+ if pick.purchase_id:
+ purchase_obj.write(cr, uid, [pick.purchase_id.id], {
+ 'invoice_ids': [(4, res[pick_id])]}, context=context)
+ return res
+
+stock_invoice_onshipping()
+
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: