From d39a918143807003e1f89aac58673731b0701f74 Mon Sep 17 00:00:00 2001 From: Cedric Snauwaert Date: Wed, 29 May 2013 11:51:50 +0200 Subject: [PATCH 01/56] [FIX]purchase_requisition: add button and a new state in purchase order workflow and changes in tender process bzr revid: csn@openerp.com-20130529095150-4f0g1vmfj9h54juu --- .../edi/purchase_order_action_data.xml | 76 ++++++++++++++++++- addons/purchase/purchase.py | 24 ++++-- addons/purchase/purchase_view.xml | 32 +++++--- addons/purchase/purchase_workflow.xml | 20 ++++- addons/purchase_requisition/__openerp__.py | 1 + .../purchase_requisition.py | 74 +++++++++++++++++- .../purchase_requisition_view.xml | 54 ++++++++++++- .../purchase_requisition/wizard/__init__.py | 1 + .../wizard/bid_line_qty.py | 41 ++++++++++ .../wizard/bid_line_qty_view.xml | 31 ++++++++ .../wizard/purchase_requisition_partner.py | 1 - .../purchase_requisition_partner_view.xml | 1 + 12 files changed, 331 insertions(+), 25 deletions(-) create mode 100644 addons/purchase_requisition/wizard/bid_line_qty.py create mode 100644 addons/purchase_requisition/wizard/bid_line_qty_view.xml diff --git a/addons/purchase/edi/purchase_order_action_data.xml b/addons/purchase/edi/purchase_order_action_data.xml index 3b02be574ef..ca206851170 100644 --- a/addons/purchase/edi/purchase_order_action_data.xml +++ b/addons/purchase/edi/purchase_order_action_data.xml @@ -18,7 +18,7 @@ - Purchase Order - Send by mail + RFQ - Send by mail ${object.validator.email or ''} ${object.company_id.name} Order (Ref ${object.name or 'n/a' }) ${object.partner_id.id} @@ -34,6 +34,80 @@

Here is a ${object.state in ('draft', 'sent') and 'request for quotation' or 'purchase order confirmation'} from ${object.company_id.name}:

+

+   REFERENCES
+   Order number: ${object.name}
+   Order date: ${object.date_order}
+ % if object.origin: +   Order reference: ${object.origin}
+ % endif + % if object.partner_ref: +   Your reference: ${object.partner_ref}
+ % endif + % if object.validator: +   Your contact: ${object.validator.name} + % endif +

+ +
+

If you have any question, do not hesitate to contact us.

+

Thank you!

+
+
+
+

+ ${object.company_id.name}

+
+
+ + % if object.company_id.street: + ${object.company_id.street}
+ % endif + % if object.company_id.street2: + ${object.company_id.street2}
+ % endif + % if object.company_id.city or object.company_id.zip: + ${object.company_id.zip} ${object.company_id.city}
+ % endif + % if object.company_id.country_id: + ${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''}
+ % endif +
+ % if object.company_id.phone: +
+ Phone:  ${object.company_id.phone} +
+ % endif + % if object.company_id.website: +
+ Web : ${object.company_id.website} +
+ %endif +

+
+ + ]]> +
+ + + + + Purchase Order - Send by mail + ${object.validator.email or ''} + ${object.company_id.name} Order (Ref ${object.name or 'n/a' }) + ${object.partner_id.id} + + + + PO_${(object.name or '').replace('/','_')} + ${object.partner_id.lang} + + +

Hello ${object.partner_id.name},

+ +

Here is a ${object.state in ('draft', 'sent') and 'request for quotation' or 'purchase order confirmation'} from ${object.company_id.name}:

+

  REFERENCES
  Order number: ${object.name}
diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index a7973a40a47..407557a0e7c 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -154,8 +154,9 @@ class purchase_order(osv.osv): STATE_SELECTION = [ ('draft', 'Draft PO'), ('sent', 'RFQ Sent'), + ('bid', 'Bid Received'), ('confirmed', 'Waiting Approval'), - ('approved', 'Purchase Order'), + ('approved', 'Purchase Confirmed'), ('except_picking', 'Shipping Exception'), ('except_invoice', 'Invoice Exception'), ('done', 'Done'), @@ -226,6 +227,8 @@ class purchase_order(osv.osv): 'create_uid': fields.many2one('res.users', 'Responsible'), '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'), + 'bid_date': fields.date('Bid Received On', readonly=True, help="Date on which the bid was received"), + 'bid_validity': fields.date('Bid Valid Until', help="Date on which the bid expired"), } _defaults = { 'date_order': fields.date.context_today, @@ -395,6 +398,9 @@ class purchase_order(osv.osv): self.write(cr, uid, ids, {'state': 'approved', 'date_approve': fields.date.context_today(self,cr,uid,context=context)}) return True + def wkf_bid_received(self, cr, uid, ids, context=None): + return self.write(cr, uid, ids, {'state':'bid', 'bid_date': fields.date.context_today(self,cr,uid,context=context)}) + def print_confirm(self,cr,uid,ids,context=None): print "Confirmed" @@ -408,9 +414,14 @@ class purchase_order(osv.osv): ''' This function opens a window to compose an email, with the edi purchase template message loaded by default ''' + if not context: + context= {} 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] + if context.get('send_rfq', False): + template_id = ir_model_data.get_object_reference(cr, uid, 'purchase', 'email_template_edi_purchase')[1] + else: + template_id = ir_model_data.get_object_reference(cr, uid, 'purchase', 'email_template_edi_purchase_done')[1] except ValueError: template_id = False try: @@ -864,7 +875,8 @@ class purchase_order_line(osv.osv): '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"), + 'lead_time': fields.float('Delivery Lead Time', help="Average delay in days to deliver this product."), } _defaults = { @@ -1195,8 +1207,10 @@ class mail_mail(osv.Model): 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) + obj = self.pool.get('purchase.order').browse(cr, uid, mail.res_id, context=context) + if obj.state == 'draft': + 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) diff --git a/addons/purchase/purchase_view.xml b/addons/purchase/purchase_view.xml index 23c153b0ae8..aed9bfd66f2 100644 --- a/addons/purchase/purchase_view.xml +++ b/addons/purchase/purchase_view.xml @@ -161,28 +161,31 @@

-

-

@@ -208,6 +211,7 @@ + @@ -249,6 +253,12 @@ + + + + + +
@@ -431,6 +441,7 @@ + purchase.order.line.form2 purchase.order.line @@ -483,8 +494,8 @@ - - + + @@ -518,6 +529,7 @@

+ tree diff --git a/addons/purchase/purchase_workflow.xml b/addons/purchase/purchase_workflow.xml index 11936db0bed..d4713044df7 100644 --- a/addons/purchase/purchase_workflow.xml +++ b/addons/purchase/purchase_workflow.xml @@ -19,6 +19,12 @@ function write({'state':'sent'}) + + + bid + function + wkf_bid_received() + confirmed @@ -103,16 +109,26 @@ send_rfq - - + + purchase_confirm + + + + bid_received + purchase_cancel + + + + purchase_cancel + diff --git a/addons/purchase_requisition/__openerp__.py b/addons/purchase_requisition/__openerp__.py index 2ba0ad95e09..03a7cf2362c 100644 --- a/addons/purchase_requisition/__openerp__.py +++ b/addons/purchase_requisition/__openerp__.py @@ -36,6 +36,7 @@ keep track and order all your purchase orders. 'demo': ['purchase_requisition_demo.xml'], 'data': ['security/purchase_tender.xml', 'wizard/purchase_requisition_partner_view.xml', + 'wizard/bid_line_qty_view.xml', 'purchase_requisition_data.xml', 'purchase_requisition_view.xml', 'purchase_requisition_report.xml', diff --git a/addons/purchase_requisition/purchase_requisition.py b/addons/purchase_requisition/purchase_requisition.py index f773bbd8505..1634568d197 100644 --- a/addons/purchase_requisition/purchase_requisition.py +++ b/addons/purchase_requisition/purchase_requisition.py @@ -32,6 +32,18 @@ class purchase_requisition(osv.osv): _name = "purchase.requisition" _description="Purchase Requisition" _inherit = ['mail.thread', 'ir.needaction_mixin'] + + def _get_po_line(self, cr, uid, ids, field_names, arg=None, context=None): + result = {} + if not ids: return result + for id in ids: + result.setdefault(id, []) + for element in self.browse(cr, uid, ids, context=context): + for po in element.purchase_ids: + for po_line in po.order_line: + result[po_line.order_id.requisition_id.id].append(po_line.id) + return result + _columns = { 'name': fields.char('Requisition Reference', size=32,required=True), 'origin': fields.char('Source Document', size=32), @@ -42,9 +54,10 @@ class purchase_requisition(osv.osv): 'description': fields.text('Description'), 'company_id': fields.many2one('res.company', 'Company', required=True), 'purchase_ids' : fields.one2many('purchase.order','requisition_id','Purchase Orders',states={'done': [('readonly', True)]}), + 'po_line_ids': fields.function(_get_po_line, method=True, type='one2many', relation='purchase.order.line', string='Products by supplier'), 'line_ids' : fields.one2many('purchase.requisition.line','requisition_id','Products to Purchase',states={'done': [('readonly', True)]}), 'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse'), - 'state': fields.selection([('draft','New'),('in_progress','Sent to Suppliers'),('cancel','Cancelled'),('done','Purchase Done')], + 'state': fields.selection([('draft','New'),('in_progress','Sent to Suppliers'),('open','Choose Lines'),('cancel','Cancelled'),('done','Purchase Done')], 'Status', track_visibility='onchange', required=True) } _defaults = { @@ -77,10 +90,14 @@ class purchase_requisition(osv.osv): def tender_in_progress(self, cr, uid, ids, context=None): return self.write(cr, uid, ids, {'state':'in_progress'} ,context=context) + def tender_open(self, cr, uid, ids, context=None): + return self.write(cr, uid, ids, {'state':'open'} ,context=context) + def tender_reset(self, cr, uid, ids, context=None): return self.write(cr, uid, ids, {'state': 'draft'}) def tender_done(self, cr, uid, ids, context=None): + self.generate_po(cr, uid, ids, context=context) return self.write(cr, uid, ids, {'state':'done', 'date_end':time.strftime('%Y-%m-%d %H:%M:%S')}, context=context) def _planned_date(self, requisition, delay=0.0): @@ -115,6 +132,25 @@ class purchase_requisition(osv.osv): date_planned = self._planned_date(requisition_line.requisition_id, seller_delay) return seller_price, qty, default_uom_po_id, date_planned + def open_product_line(self, cr, uid, ids, context=None): + """ This opens product line view to view all lines from the different quotations, groupby default by product and partner to show comparaison + between supplier price + @return: the product line tree view + """ + if context is None: + context = {} + res = self.pool.get('ir.actions.act_window').for_xml_id(cr, uid ,'purchase_requisition','purchase_line_tree', context=context) + res['context'] = context + po_ids_browse = self.browse(cr, uid, ids, context=context)[0].po_line_ids + po_ids=[] + for po in po_ids_browse: + po_ids.append(po.id) + res['context'].update({ + 'search_default_groupby_product' : True, + }) + res['domain'] = [('id','in', po_ids)] + return res + def make_purchase_order(self, cr, uid, ids, partner_id, context=None): """ Create New RFQ for Supplier @@ -163,6 +199,20 @@ class purchase_requisition(osv.osv): return res + def generate_po(self, cr, uid, id, context=None): + """ + Generate all purchase order based on selected lines, should only be called on one tender at a time + """ + id_per_supplier = {} + for po_line in self.browse(cr, uid, id, context=context)[0].po_line_ids: + if po_line.state == 'confirmed': + partner = po_line.partner_id.id + if id_per_supplier.get(partner): + id_per_supplier[partner].append(po_line.id) + else: + id_per_supplier[partner] = [po_line.id] + + #TODO generate po based on supplier and check if a draft po is complete before creating a new one class purchase_requisition_line(osv.osv): @@ -174,7 +224,9 @@ class purchase_requisition_line(osv.osv): 'product_id': fields.many2one('product.product', 'Product' ), 'product_uom_id': fields.many2one('product.uom', 'Product Unit of Measure'), 'product_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure')), - 'requisition_id' : fields.many2one('purchase.requisition','Purchase Requisition', ondelete='cascade'), + 'po_line_buy': fields.many2one('purchase.order.line', 'Purchase Order Line'), + 'requisition_id': fields.many2one('purchase.requisition','Purchase Requisition', ondelete='cascade'), + 'po_line_ids': fields.related('requisition_id', 'po_line_ids', string='PO lines', readonly=True, type="one2many"), 'company_id': fields.related('requisition_id','company_id',type='many2one',relation='res.company',string='Company', store=True, readonly=True), } @@ -218,6 +270,24 @@ class purchase_order(osv.osv): purchase_order() +class purchase_order_line(osv.osv): + _inherit = 'purchase.order.line' + + #TODO add field function to get same value as product_qty wherever quantity_bid is zero, with an inverse fct to write on quantity_bid + _columns= { + 'quantity_bid': fields.float('Quantity Bid', digits_compute=dp.get_precision('Product Unit of Measure')), + } + + def action_draft(self, cr, uid, ids, context=None): + self.write(cr, uid, ids, {'state': 'draft'}, context=context) + + def action_confirm(self, cr, uid, ids, context=None): + super(purchase_order_line, self).action_confirm(cr, uid, ids, context=context) + for element in self.browse(cr, uid, ids, context=context): + if not element.quantity_bid: + self.write(cr, uid, ids, {'quantity_bid': element.product_qty}, context=context) + return True + class product_product(osv.osv): _inherit = 'product.product' diff --git a/addons/purchase_requisition/purchase_requisition_view.xml b/addons/purchase_requisition/purchase_requisition_view.xml index 55427de6573..739a878ab12 100644 --- a/addons/purchase_requisition/purchase_requisition_view.xml +++ b/addons/purchase_requisition/purchase_requisition_view.xml @@ -31,12 +31,17 @@
+
+
+
+ +