Merge lp:~camptocamp/openerp-humanitarian-ngo/add-bid_selected_state_on_po-nbi into lp:openerp-humanitarian-ngo/purchase-wkfl

Proposed by Nicolas Bessi - Camptocamp
Status: Merged
Approved by: Yannick Vaucher @ Camptocamp
Approved revision: 79
Merged at revision: 73
Proposed branch: lp:~camptocamp/openerp-humanitarian-ngo/add-bid_selected_state_on_po-nbi
Merge into: lp:openerp-humanitarian-ngo/purchase-wkfl
Diff against target: 253 lines (+96/-25)
6 files modified
purchase_extended/model/purchase_order.py (+8/-2)
purchase_extended/test/process/bid2order.yml (+8/-3)
purchase_extended/workflow/purchase_order.xml (+14/-0)
purchase_requisition_extended/model/purchase_order.py (+2/-0)
purchase_requisition_extended/model/purchase_requisition.py (+63/-20)
purchase_requisition_extended/view/purchase_requisition.xml (+1/-0)
To merge this branch: bzr merge lp:~camptocamp/openerp-humanitarian-ngo/add-bid_selected_state_on_po-nbi
Reviewer Review Type Date Requested Status
Yannick Vaucher @ Camptocamp code review, no tests Approve
Romain Deheele - Camptocamp (community) code review Approve
Joël Grand-Guillaume @ camptocamp code review + test Approve
Review via email: mp+200799@code.launchpad.net

Commit message

[ADD] a bid selected state on PO
[FIX] confirmation of cost estimate. PO in state bid will be cancel too

Description of the change

Add a bid selected state on PO
Fix confirmation of cost estimate. PO in state bid will be cancel too

To post a comment you must log in.
Revision history for this message
Nicolas Bessi - Camptocamp (nbessi-c2c-deactivatedaccount) wrote :

Add a bid selected state on PO
Fix confirmation of cost estimate. PO in state bid will be cancel too

74. By Nicolas Bessi <email address hidden>

[FIX] on change product line on po line to allows manual creation of purchase order

75. By Nicolas Bessi <email address hidden>

[FIX] test do not use draftbid state but sent state as we can not send context to the server

76. By Joël Grand-Guillaume @ camptocamp

[ADD] The pricelist on the requisition and bid creation process

77. By Joël Grand-Guillaume @ camptocamp

[IMP] Add a pricelist in tendering process

78. By Nicolas Bessi - Camptocamp

[MRG] from head

Revision history for this message
Joël Grand-Guillaume @ camptocamp (jgrandguillaume-c2c) wrote :

LGTM, Thanks !

review: Approve (code review + test)
Revision history for this message
Romain Deheele - Camptocamp (romaindeheele) wrote :

LGTM,

Romain

review: Approve (code review)
79. By Yannick Vaucher @ Camptocamp

[IMP] assign single dict value using key instead of using update

Revision history for this message
Yannick Vaucher @ Camptocamp (yvaucher-c2c) wrote :

LGTM

review: Approve (code review, no tests)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'purchase_extended/model/purchase_order.py'
--- purchase_extended/model/purchase_order.py 2013-11-13 08:49:37 +0000
+++ purchase_extended/model/purchase_order.py 2014-02-06 13:54:03 +0000
@@ -14,6 +14,7 @@
14 ('sent', 'RFQ Sent'),14 ('sent', 'RFQ Sent'),
15 ('draftbid', 'Draft Bid'), # added15 ('draftbid', 'Draft Bid'), # added
16 ('bid', 'Bid Encoded'), # Bid Received renamed into Bid Encoded16 ('bid', 'Bid Encoded'), # Bid Received renamed into Bid Encoded
17 ('bid_selected', 'Bid selected'), # added
17 ('draftpo', 'Draft PO'), # added18 ('draftpo', 'Draft PO'), # added
18 ('confirmed', 'Waiting Approval'),19 ('confirmed', 'Waiting Approval'),
19 ('approved', 'Purchase Confirmed'),20 ('approved', 'Purchase Confirmed'),
@@ -236,17 +237,22 @@
236 value['value']['dest_address_id'] = dest_id237 value['value']['dest_address_id'] = dest_id
237 return value238 return value
238239
240 def po_tender_requisition_selected(self, cr, uid, ids, context=None):
241 """Workflow function that write state 'bid selected'"""
242 return self.write(cr, uid, ids, {'state': 'bid_selected'},
243 context=context)
244
239245
240class purchase_order_line(orm.Model):246class purchase_order_line(orm.Model):
241 _inherit = 'purchase.order.line'247 _inherit = 'purchase.order.line'
242248
243 def onchange_product_id(self, cr, uid, ids, pricelist_id, product_id, qty, uom_id,249 def onchange_product_id(self, cr, uid, ids, pricelist_id, product_id, qty, uom_id,
244 partner_id, date_order=False, fiscal_position_id=False, date_planned=False,250 partner_id, date_order=False, fiscal_position_id=False, date_planned=False,
245 name=False, price_unit=False, context=None, state='draft', type='rfq', **kwargs):251 name=False, price_unit=False, context=None, state='draftpo', type='purchase', **kwargs):
246 res = super(purchase_order_line, self).onchange_product_id(cr, uid, ids,252 res = super(purchase_order_line, self).onchange_product_id(cr, uid, ids,
247 pricelist_id, product_id, qty, uom_id, partner_id, date_order,253 pricelist_id, product_id, qty, uom_id, partner_id, date_order,
248 fiscal_position_id, date_planned, name, price_unit, context)254 fiscal_position_id, date_planned, name, price_unit, context)
249 if state == 'draft' and type == 'bid':255 if state == 'draft' and type == 'rfq':
250 res['value'].update({'price_unit': 0})256 res['value'].update({'price_unit': 0})
251 elif state in ('sent', 'draftbid', 'bid'):257 elif state in ('sent', 'draftbid', 'bid'):
252 if 'price_unit' in res['value']:258 if 'price_unit' in res['value']:
253259
=== modified file 'purchase_extended/test/process/bid2order.yml'
--- purchase_extended/test/process/bid2order.yml 2013-11-01 11:44:19 +0000
+++ purchase_extended/test/process/bid2order.yml 2014-02-06 13:54:03 +0000
@@ -22,11 +22,16 @@
22 date_planned: '2013-08-30'22 date_planned: '2013-08-30'
23 price_unit: 52.5323 price_unit: 52.53
24-24-
25 Type must be 'bid' and the total untaxed amount of the RFQ must be computed.25 I print the RFQ.
26-
27 !python {model: purchase.order}: |
28 self.print_quotation(cr, uid, [ref("purchase_order_ext_bid2order1")])
29-
30 Type must be 'rfq' and the total untaxed amount of the RFQ must be computed.
26-31-
27 !assert {model: purchase.order, id: purchase_order_ext_bid2order1, string: The amount of RFQ is not correctly computed}:32 !assert {model: purchase.order, id: purchase_order_ext_bid2order1, string: The amount of RFQ is not correctly computed}:
28 - type == 'bid'33 - type == 'rfq'
29 - state == 'draftbid'34 - state == 'sent'
30 - round(sum([l.price_subtotal for l in order_line]), 2) == round(amount_untaxed, 2)35 - round(sum([l.price_subtotal for l in order_line]), 2) == round(amount_untaxed, 2)
31-36-
32 I run the 'Bid encoded' wizard. I fill the date.37 I run the 'Bid encoded' wizard. I fill the date.
3338
=== modified file 'purchase_extended/workflow/purchase_order.xml'
--- purchase_extended/workflow/purchase_order.xml 2013-08-07 12:39:30 +0000
+++ purchase_extended/workflow/purchase_order.xml 2014-02-06 13:54:03 +0000
@@ -55,6 +55,20 @@
55 <field name="signal">purchase_cancel</field>55 <field name="signal">purchase_cancel</field>
56 </record>56 </record>
5757
58 <record id="act_po_requisition_selected" model="workflow.activity">
59 <field name="wkf_id" ref="purchase.purchase_order"/>
60 <field name="name">Bid selected</field>
61 <field name="kind">function</field>
62 <field name="action">po_tender_requisition_selected()</field>
63 <field name="flow_stop">True</field>
64 </record>
65 <record id="trans_po_requisition_selected" model="workflow.transition">
66 <field name="act_from" ref="purchase.act_bid"/>
67 <field name="act_to" ref="act_po_requisition_selected"/>
68 <field name="signal">select_requisition</field>
69 </record>
70
71
58 <!-- Rename some activities to make the workflow clear -->72 <!-- Rename some activities to make the workflow clear -->
59 <record id="purchase.act_draft" model="workflow.activity">73 <record id="purchase.act_draft" model="workflow.activity">
60 <field name="name">draft RFQ</field>74 <field name="name">draft RFQ</field>
6175
=== modified file 'purchase_requisition_extended/model/purchase_order.py'
--- purchase_requisition_extended/model/purchase_order.py 2014-02-06 09:49:41 +0000
+++ purchase_requisition_extended/model/purchase_order.py 2014-02-06 13:54:03 +0000
@@ -62,6 +62,8 @@
62 'incoterm_address': requisition.req_incoterm_address,62 'incoterm_address': requisition.req_incoterm_address,
63 'transport_mode_id': requisition.req_transport_mode_id,63 'transport_mode_id': requisition.req_transport_mode_id,
64 })64 })
65 if requisition.pricelist_id:
66 values.update({'pricelist_id': requisition.pricelist_id.id})
65 return values67 return values
6668
67 def copy(self, cr, uid, id, default=None, context=None):69 def copy(self, cr, uid, id, default=None, context=None):
6870
=== modified file 'purchase_requisition_extended/model/purchase_requisition.py'
--- purchase_requisition_extended/model/purchase_requisition.py 2013-11-01 11:44:19 +0000
+++ purchase_requisition_extended/model/purchase_requisition.py 2014-02-06 13:54:03 +0000
@@ -25,15 +25,33 @@
25 domain=[('type', 'in', ('rfq', 'bid'))]),25 domain=[('type', 'in', ('rfq', 'bid'))]),
26 # new26 # new
27 'req_validity': fields.date("Requested Bid's End of Validity",27 'req_validity': fields.date("Requested Bid's End of Validity",
28 help="Default value requested to "28 help="Requested validity period requested to the bidder, "
29 "the supplier."),29 "i.e. please send bids that stay valid until that "
30 "date.\n The bidder is allowed to send a bid with "
31 "another validity end date that gets encoded in the "
32 "bid."),
30 'bid_tendering_mode': fields.selection([('open', 'Open'),33 'bid_tendering_mode': fields.selection([('open', 'Open'),
31 ('restricted', 'Restricted')],34 ('restricted', 'Restricted')],
32 'Call for Bids Mode'),35 'Call for Bids Mode'),
36 help="- Restricted : you select yourself the "
37 "bidders and generate a RFQ for each of "
38 "those. \n"
39 "- Open : anybody can bid (you have to "
40 "advertise the call for bids) and you "
41 "directly encode the bids you received. "
42 "You are still able to generate RFQ if "
43 "you want to contact usual bidders."),
33 'bid_receipt_mode': fields.selection([('open', 'Open'),44 'bid_receipt_mode': fields.selection([('open', 'Open'),
34 ('sealed', 'Sealed')],45 ('sealed', 'Sealed')],
35 'Bid Receipt Mode',46 'Bid Receipt Mode',
36 required=True),47 required=True),
48 help="- Open : The bids can be opened when "
49 "received and encoded. \n"
50 "- Closed : The bids can be marked as "
51 "received but they have to be opened \n"
52 "all at the same time after an opening "
53 "ceremony (probably specific to public "
54 "sector)."),
37 'consignee_id': fields.many2one('res.partner',55 'consignee_id': fields.many2one('res.partner',
38 'Consignee',56 'Consignee',
39 help="Person responsible of delivery"),57 help="Person responsible of delivery"),
@@ -55,6 +73,13 @@
55 'account.payment.term',73 'account.payment.term',
56 'Requested Payment Term',74 'Requested Payment Term',
57 help="Default value requested to the supplier."),75 help="Default value requested to the supplier."),
76 'pricelist_id': fields.many2one('product.pricelist',
77 'Pricelist',
78 help="If set that pricelist will be used to generate the RFQ."
79 "Mostely used to ask a requisition in a given currency."),
80 'date_end': fields.datetime('Bid Submission Deadline',
81 help="All bids received after that date won't be valid "
82 " (probably specific to public sector)."),
58 }83 }
59 _defaults = {84 _defaults = {
60 'bid_receipt_mode': 'open',85 'bid_receipt_mode': 'open',
@@ -83,6 +108,8 @@
83 'incoterm_id': requisition.req_incoterm_id.id,108 'incoterm_id': requisition.req_incoterm_id.id,
84 'incoterm_address': requisition.req_incoterm_address,109 'incoterm_address': requisition.req_incoterm_address,
85 })110 })
111 if requisition.pricelist_id:
112 values['pricelist_id'] = requisition.pricelist_id.id
86 return values113 return values
87114
88 def _prepare_purchase_order_line(self, cr, uid, requisition,115 def _prepare_purchase_order_line(self, cr, uid, requisition,
@@ -142,18 +169,34 @@
142 context=context)169 context=context)
143 return super(PurchaseRequisition, self).generate_po(cr, uid, [ids], context=context)170 return super(PurchaseRequisition, self).generate_po(cr, uid, [ids], context=context)
144171
172 def quotation_selected(self, cr, uid, quotation, context=None):
173 """Predicate that checks if a quotation has at least one line chosen
174 :param quotation: record of 'purchase.order'
175
176 :returns: True if one line has been chosen
177
178 """
179 # This topic is subject to changes
180 return quotation.bid_partial
181
145 def cancel_quotation(self, cr, uid, tender, context=None):182 def cancel_quotation(self, cr, uid, tender, context=None):
146 """183 """
147 Called from generate_po. Cancel only draft and sent rfq184 Called from generate_po. Cancel only draft and sent rfq
148 """185 """
149 po = self.pool.get('purchase.order')186 po = self.pool.get('purchase.order')
150 wf_service = netsvc.LocalService("workflow")187 wf_service = netsvc.LocalService("workflow")
188 tender.refresh()
151 for quotation in tender.purchase_ids:189 for quotation in tender.purchase_ids:
152 if quotation.state in ['draft', 'sent']:190 if quotation.state in ['draft', 'sent', 'bid']:
153 wf_service.trg_validate(uid, 'purchase.order', quotation.id, 'purchase_cancel', cr)191 if self.quotation_selected(cr, uid, quotation, context=context):
154 po.message_post(cr, uid, [quotation.id],192 wf_service.trg_validate(uid, 'purchase.order', quotation.id,
155 body=_('Canceled by the call for bids associated to this request for quotation.'),193 'select_requisition', cr)
156 context=context)194 else:
195 wf_service.trg_validate(uid, 'purchase.order', quotation.id, 'purchase_cancel', cr)
196 po.message_post(cr, uid, [quotation.id],
197 body=_('Canceled by the call for bids associated'
198 ' to this request for quotation.'),
199 context=context)
157200
158 return True201 return True
159202
@@ -226,19 +269,6 @@
226 res['domain'] = expression.AND([eval(res.get('domain', [])), [('requisition_id', 'in', ids)]])269 res['domain'] = expression.AND([eval(res.get('domain', [])), [('requisition_id', 'in', ids)]])
227 return res270 return res
228271
229 def open_product_line(self, cr, uid, ids, context=None):
230 """ Filter to show only lines from bids received. Group by requisition line instead of product for unicity
231 """
232 res = super(PurchaseRequisition, self).open_product_line(cr, uid, ids, context=context)
233 ctx = res.setdefault('context', {})
234 if 'search_default_groupby_product' in ctx:
235 del ctx['search_default_groupby_product']
236 if 'search_default_hide_cancelled' in ctx:
237 del ctx['search_default_hide_cancelled']
238 ctx['search_default_groupby_requisitionline'] = True
239 ctx['search_default_showbids'] = True
240 return res
241
242 def close_callforbids(self, cr, uid, ids, context=None):272 def close_callforbids(self, cr, uid, ids, context=None):
243 """273 """
244 Check all quantities have been sourced274 Check all quantities have been sourced
@@ -289,6 +319,19 @@
289 'context': ctx,319 'context': ctx,
290 }320 }
291321
322 def open_product_line(self, cr, uid, ids, context=None):
323 """ Filter to show only lines from bids received. Group by requisition line instead of product for unicity
324 """
325 res = super(PurchaseRequisition, self).open_product_line(cr, uid, ids, context=context)
326 ctx = res.setdefault('context', {})
327 if 'search_default_groupby_product' in ctx:
328 del ctx['search_default_groupby_product']
329 if 'search_default_hide_cancelled' in ctx:
330 del ctx['search_default_hide_cancelled']
331 ctx['search_default_groupby_requisitionline'] = True
332 ctx['search_default_showbids'] = True
333 return res
334
292 def close_callforbids_ok(self, cr, uid, ids, context=None):335 def close_callforbids_ok(self, cr, uid, ids, context=None):
293 wf_service = netsvc.LocalService("workflow")336 wf_service = netsvc.LocalService("workflow")
294 for id in ids:337 for id in ids:
295338
=== modified file 'purchase_requisition_extended/view/purchase_requisition.xml'
--- purchase_requisition_extended/view/purchase_requisition.xml 2013-08-08 09:31:25 +0000
+++ purchase_requisition_extended/view/purchase_requisition.xml 2014-02-06 13:54:03 +0000
@@ -21,6 +21,7 @@
21 </xpath>21 </xpath>
22 <xpath expr="//field[@name='user_id']" position="after">22 <xpath expr="//field[@name='user_id']" position="after">
23 <field name="bid_tendering_mode" attrs="{'readonly': [('state','not in',('draft'))]}" required="1"/>23 <field name="bid_tendering_mode" attrs="{'readonly': [('state','not in',('draft'))]}" required="1"/>
24 <field name="pricelist_id" attrs="{'readonly': [('state','not in',('draft'))]}"/>
24 </xpath>25 </xpath>
25 <separator string="Requests for Quotation" position="attributes">26 <separator string="Requests for Quotation" position="attributes">
26 <attribute name="string">Requests for Quotation / Bids</attribute>27 <attribute name="string">Requests for Quotation / Bids</attribute>

Subscribers

People subscribed via source and target branches