############################################################################## # # Copyright (c) 2004 TINY SPRL. (http://tiny.be) All Rights Reserved. # # $Id: purchase.py 1005 2005-07-25 08:41:42Z nicoe $ # # WARNING: This program as such is intended to be used by professional # programmers who take the whole responsability of assessing all potential # consequences resulting from its eventual inadequacies and bugs # End users who are looking for a ready-to-use solution with commercial # garantees and support are strongly adviced to contract a Free Software # Service Company # # This program is Free Software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # ############################################################################## from osv import fields from osv import osv import time import netsvc import ir from mx import DateTime # # 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): res[order.id] = 0 for oline in order.order_line: res[order.id] += oline.price_unit * oline.product_qty return res _columns = { 'name': fields.char('Order Description', size=64, required=True), 'origin': fields.char('Origin', size=64), 'ref': fields.char('Order Reference', size=64), 'partner_ref': fields.char('Partner Reference', size=64), 'date_order':fields.date('Date Ordered', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)]}), 'date_approve':fields.date('Date Approved'), 'partner_id':fields.many2one('res.partner', 'Partner', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)]}, change_default=True, relate=True), 'partner_address_id':fields.many2one('res.partner.address', 'Address', required=True, states={'posted':[('readonly',True)]}), 'dest_address_id':fields.many2one('res.partner.address', 'Destination Address', states={'posted':[('readonly',True)]}), 'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', states={'posted':[('readonly',True)]}, relate=True), 'location_id': fields.many2one('stock.location', 'Delivery destination', required=True), 'project_id':fields.many2one('account.project', 'Profit/Cost center', states={'posted':[('readonly',True)]}), 'pricelist_id':fields.many2one('product.pricelist', 'Pricelist', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)]}), 'state': fields.selection([('draft','draft'), ('wait', 'Waiting'), ('confirmed','confirmed'),('approved','approved'),('except_ship','Shipping Exception'),('except_invoice','Invoice Exception'),('done','done'),('cancel','cancel')], 'Order State', readonly=True), 'order_line': fields.one2many('purchase.order.line', 'order_id', 'Order State', states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)]}), 'validator' : fields.many2one('res.users', 'Validated by', readonly=True), 'notes': fields.text('Notes'), 'invoice_id': fields.many2one('account.invoice', 'Invoice', readonly=True), 'picking_id': fields.many2one('stock.picking', 'Picking list', readonly=True), 'shipped':fields.boolean('Received', readonly=True), 'invoiced':fields.boolean('Invoiced & Paid', readonly=True), 'amount' : fields.function(_calc_amount, method=True, string="Total amount"), } _defaults = { 'date_order': lambda *a: time.strftime('%Y-%m-%d'), 'state': lambda *a: 'draft', 'name': lambda *a: 'PO', 'shipped': lambda *a: 0, 'invoiced': lambda *a: 0 } _name = "purchase.order" _description = "Purchase order" 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 = ir.ir_get(cr,uid,'meta','stock.lot.customer',[('res.partner',part_id)])[0][2] return {'value':{'location_id': loc_id, 'warehouse_id': False}} def onchange_warehouse_id(self, cr, uid, ids, warehouse_id): if not warehouse_id: return {} res = self.pool.get('stock.warehouse').read(cr, uid, [warehouse_id], ['lot_input_id'])[0]['lot_input_id'][0] return {'value':{'location_id': res, 'dest_address_id': False}} def onchange_partner_id(self, cr, uid, ids, part): if not part: return {'value':{'partner_address_id': False}} addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['contact']) pricelist=ir.ir_get(cr,uid,'meta','product.pricelist.purchase',[('res.partner',part)])[0][2] return {'value':{'partner_address_id': addr['contact'], 'pricelist_id': pricelist}} def wkf_approve_order(self, cr, uid, ids): self.write(cr, uid, ids, {'state':'approved', 'date_approve':time.strftime('%Y-%m-%d')}) return True def wkf_confirm_order(self, cr, uid, ids, context={}): for po in self.browse(cr, uid, ids): if self.pool.get('res.partner.event.type').check(cr, uid, 'purchase_open'): self.pool.get('res.partner.event').create(cr, uid, {'name':'Purchase Order: '+po.name, 'partner_id':po.partner_id.id, 'date':time.strftime('%Y-%m-%d %H:%M:%S'), 'user_id':uid, 'partner_type':'retailer', 'probability': 1.0, 'planned_cost':po.amount}) current_name = self.name_get(cr, uid, ids)[0][1] self.write(cr, uid, ids, {'ref' : '%s' % (self.pool.get('ir.sequence').get(cr, uid, 'purchase.order'),), 'state' : 'confirmed', 'validator' : uid}) return True def wkf_warn_buyer(self, cr, uid, ids): self.write(cr, uid, ids, {'state' : 'wait', 'validator' : uid}) request = osv.osv_pools.get('res.request') for po in self.browse(cr, uid, ids): managers = [] for oline in po.order_line: manager = oline.product_id.product_manager 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", 'act_from' : uid, 'act_to' : manager_id, '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 action_invoice_create(self,cr, uid, ids, *args): res = False for o in self.browse(cr,uid,ids): il=[] for ol in o.order_line: opt=[('id',str(ol.product_id.id or -1)),('uid',str(uid))] place = [] place.append( ('product.product',ol.product_id.id) ) if ol.product_id.categ_id.id: place.append( ('product.category', ol.product_id.categ_id.id) ) a=ir.ir_get(cr,uid,'meta','account.expense',place)[0][2] il.append( (0,False,{'name':ol.name, 'account_id':a, 'price_unit':ol.price_unit or 0.0, 'quantity':ol.product_qty, 'invoice_line_tax_id':[(6,0,[x.id for x in ol.taxes_id])] }) ) opt=[('id',str(o.partner_id.id or -1)), ('uid',str(uid))] a=ir.ir_get(cr,uid,'meta','account.payable',[('res.partner',o.partner_id.id or False)])[0][2] inv={ 'name': o.name, 'reference': "P%dPO%d"%(o.partner_id.id,o.id), 'account_id': a, 'type': 'in_invoice', 'partner_id': o.partner_id.id, 'address_invoice_id': o.partner_address_id.id, 'address_contact_id': o.partner_address_id.id, 'invoice_line': il, } inv_id = self.pool.get('account.invoice').create(cr, uid, inv) self.write(cr,uid,[o.id],{'invoice_id':inv_id}) res = inv_id return res 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=='product': return True return False def action_picking_create(self,cr, uid, ids, *args): picking_id = False orders = self.browse(cr, uid, ids) for order in orders: loc_id = ir.ir_get(cr,uid,'meta','stock.lot.supplier',[('res.partner',order.partner_id.id)])[0][2] picking_id = self.pool.get('stock.picking').create(cr, uid, { 'name': 'PO:'+order.name, 'origin': 'PO:'+str(order.id), 'type': 'in', 'address_id': order.dest_address_id.id or order.partner_address_id.id, }) for order_line in order.order_line: if order_line.product_id.product_tmpl_id.type=='product': dest = order.location_id.id self.pool.get('stock.move').create(cr, uid, { 'name': 'PO:'+order_line.name, 'product_id': order_line.product_id.id, 'product_qty': order_line.product_qty, 'product_uom': order_line.product_uom.id, 'date_planned': order_line.date_planned, 'location_id': loc_id, 'location_dest_id': dest, 'picking_id': picking_id, 'move_dest_id': order_line.move_dest_id.id, 'state': 'assigned' }) 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}) self.write(cr,uid,[order.id],{'picking_id':picking_id}) wf_service = netsvc.LocalService("workflow") wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr) return picking_id purchase_order() class purchase_order_line(osv.osv): def _amount_line(self, cr, uid, ids, prop, unknow_none,unknow_dict): res = {} for line in self.browse(cr, uid, ids): res[line.id] = line.price_unit * line.product_qty return res _columns = { 'name': fields.char('Description', size=64, required=True), 'product_qty': fields.float('Quantity', required=True, digits=(16,2)), 'date_planned': fields.date('Date Promised', required=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), 'move_id': fields.many2one('stock.move', 'Reservation', ondelete='set null'), 'move_dest_id': fields.many2one('stock.move', 'Reservation Destination', ondelete='set null'), 'price_unit': fields.float('Unit Price', required=True), 'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal'), 'notes': fields.text('Notes'), 'order_id': fields.many2one('purchase.order', 'Order Ref') } _defaults = { 'product_qty': lambda *a: 1.0 } _table = 'purchase_order_line' _name = 'purchase.order.line' _description = 'Purchase Order line' def product_id_change(self, cr, uid, ids, pricelist, product, qty, uom): if not pricelist: raise osv.except_osv('No Pricelist !', 'You have to select a pricelist in the sale form !\n Please set one before choosing a product.') if not product: return {'value': {'price_unit': 0.0, 'name':'','notes':''}, 'domain':{'product_uom':[]}} price = self.pool.get('product.pricelist').price_get(cr,uid,[pricelist], product, qty or 1.0, 'standard')[pricelist] prod = self.pool.get('product.product').read(cr, uid, [product], ['taxes_id','name','seller_delay','uom_po_id','description_purchase'])[0] dt = (DateTime.now() + DateTime.RelativeDateTime(days=prod['seller_delay'] or 0.0)).strftime('%Y-%m-%d') res = {'value': {'price_unit': price, 'name':prod['name'], 'taxes_id':prod['taxes_id'], 'date_planned': dt,'notes':prod['description_purchase']}} domain = {} if not uom: res['value']['product_uom'] = prod['uom_po_id'][0] if res['value']['product_uom']: res2 = self.pool.get('product.uom').read(cr, uid, [res['value']['product_uom']], ['category_id']) if res2 and res2[0]['category_id']: domain = {'product_uom':[('category_id','=',res2[0]['category_id'][0])]} res['domain'] = domain return res purchase_order_line()