Skip to content

Commit

Permalink
Merge branch 'a181049447354185_awarding' of https://github.com/openpr…
Browse files Browse the repository at this point in the history
…ocurement/openprocurement.auctions.dgf into production

* 'a181049447354185_awarding' of https://github.com/openprocurement/openprocurement.auctions.dgf: (30 commits)
  Change AWARD_PAYMENT_TIME/CONTRACT_SIGNING_TIME from 11 to 20 days
  Add migration from0to1
  Fix next_check after patching contact
  Docs: Changed cancel to cancelled in graphviz & statuses sectioin (#24)
  Docs: modified tutorial according to the new Awarding 2.1 and translated it (#23)
  Docs: New Awarding 2.1 (#22)
  Allow upload file in bid if his award is pending
  A181049447354185 awarding (#21)
  Docs: update https
  Update award periods calculation
  Remove check for auction protocol when verification period is expired
  Docs: awarding 2.1
  Awarding 2.1
  Switch award to pending.payment only with auction owner auctionProtocol
  zc.buildout = 2.2.5
  Switch op.api to ea_production branch
  Docs:fix graph
  Added awarding statuses diagram, description, links to qualification, as well as  translation (#18)
  Tweaked the tutorial translation (#17)
  Docs: minor localization tweaks for tutorial
  ...
  • Loading branch information
Andrew Leitsius committed Apr 10, 2017
2 parents 8322313 + 476538b commit 4844fed
Show file tree
Hide file tree
Showing 122 changed files with 5,523 additions and 2,059 deletions.
2 changes: 1 addition & 1 deletion buildout.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ eggs =


[sources]
openprocurement.api = git https://github.com/openprocurement/openprocurement.api.git branch=document_service
openprocurement.api = git https://github.com/openprocurement/openprocurement.api.git branch=ea_production
openprocurement.auctions.flash = git https://github.com/openprocurement/openprocurement.auctions.flash.git
openprocurement.auctions.core = git https://github.com/openprocurement/openprocurement.auctions.core.git
111 changes: 72 additions & 39 deletions docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,8 @@

test_max_uid = uuid4().hex

test_auction_maximum_data = {
"title": u"футляри до державних нагород",
test_auction_maximum_data = test_auction_data.copy()
test_auction_maximum_data.update({
"title_en": u"Cases with state awards",
"title_ru": u"футляры к государственным наградам",
"procuringEntity": {
Expand All @@ -147,14 +147,6 @@
},
'kind': 'general'
},
"value": {
"amount": 500,
"currency": u"UAH"
},
"minimalStep": {
"amount": 35,
"currency": u"UAH"
},
"items": [
{
"id": test_max_uid,
Expand All @@ -171,12 +163,7 @@
"quantity": 5
}
],
"auctionPeriod": {
"startDate": (now + timedelta(days=14)).isoformat()
},
"procurementMethodType": "dgfOtherAssets",
"mode": u"test"
}
})


test_complaint_data = {'data':
Expand Down Expand Up @@ -594,8 +581,27 @@ def test_docs_tutorial(self):

self.app.authorization = ('Basic', ('broker', ''))

response = self.app.get('/auctions/{}/awards'.format(self.auction_id))
with open('docs/source/tutorial/get-awards.http', 'w') as self.app.file_obj:
response = self.app.get('/auctions/{}/awards'.format(self.auction_id))
self.assertEqual(response.status, '200 OK')
self.assertEqual(len(response.json['data']), 2)

# get waiting award
award = [i for i in response.json['data'] if i['status'] == 'pending.waiting'][0]
award_id = award['id']

with open('docs/source/qualification/award-waiting-cancel.http', 'w') as self.app.file_obj:
response = self.app.patch_json('/auctions/{}/awards/{}?acc_token={}'.format(
self.auction_id, award_id, bids_access[award['bid_id']]), {"data": {"status": "cancelled"}})
self.assertEqual(response.status, '200 OK')

# get pending award
response = self.app.get('/auctions/{}/awards'.format(self.auction_id))
award_id = [i['id'] for i in response.json['data'] if i['status'] == 'pending.verification'][0]

with open('docs/source/tutorial/bidder-auction-protocol.http', 'w') as self.app.file_obj:
response = self.app.post_json('/auctions/{}/bids/{}/documents?acc_token={}'.format(self.auction_id, bid2_id, bids_access[bid2_id]),
response = self.app.post_json('/auctions/{}/awards/{}/documents?acc_token={}'.format(self.auction_id, award_id, bids_access[bid2_id]),
{'data': {
'title': u'SignedAuctionProtocol.pdf',
'url': self.generate_docservice_url(),
Expand All @@ -605,12 +611,23 @@ def test_docs_tutorial(self):
}})
self.assertEqual(response.status, '201 Created')

response = self.app.get('/auctions/{}/awards'.format(self.auction_id))
# get pending award
award_id = [i['id'] for i in response.json['data'] if i['status'] == 'pending'][0]
with open('docs/source/tutorial/owner-auction-protocol.http', 'w') as self.app.file_obj:
response = self.app.post_json('/auctions/{}/awards/{}/documents?acc_token={}'.format(self.auction_id, award_id, owner_token),
{'data': {
'title': u'SignedAuctionProtocol.pdf',
'url': self.generate_docservice_url(),
'hash': 'md5:' + '0' * 32,
'format': 'application/pdf',
"documentType": "auctionProtocol",
}})
self.assertEqual(response.status, '201 Created')

with open('docs/source/tutorial/verify-protocol.http', 'w') as self.app.file_obj:
response = self.app.patch_json('/auctions/{}/awards/{}?acc_token={}'.format(self.auction_id, award_id, owner_token), {"data": {"status": "pending.payment"}})
self.assertEqual(response.status, '200 OK')

with open('docs/source/tutorial/confirm-qualification.http', 'w') as self.app.file_obj:
self.app.patch_json('/auctions/{}/awards/{}?acc_token={}'.format(self.auction_id, award_id, owner_token), {"data": {"status": "active"}})
response = self.app.patch_json('/auctions/{}/awards/{}?acc_token={}'.format(self.auction_id, award_id, owner_token), {"data": {"status": "active"}})
self.assertEqual(response.status, '200 OK')

response = self.app.get('/auctions/{}/contracts'.format(self.auction_id))
Expand Down Expand Up @@ -735,11 +752,11 @@ def test_docs_disqualification(self):
self.app.authorization = ('Basic', ('broker', ''))
response = self.app.post_json('/auctions/{}/bids'.format(self.auction_id),
{'data': {"qualified": True, 'tenderers': [bid["data"]["tenderers"][0]], "value": {"amount": 450}}})
bid_id = response.json['data']['id']
bid_token = response.json['access']['token']
self.initial_bids_tokens[response.json['data']['id']] = response.json['access']['token']
self.app.authorization = ('Basic', ('broker', ''))
response = self.app.post_json('/auctions/{}/bids'.format(self.auction_id),
{'data': {"qualified": True, 'tenderers': [bid["data"]["tenderers"][0]], "value": {"amount": 475}}})
self.initial_bids_tokens[response.json['data']['id']] = response.json['access']['token']
# get auction info
self.set_status('active.auction')
self.app.authorization = ('Basic', ('auction', ''))
Expand Down Expand Up @@ -769,7 +786,21 @@ def test_docs_disqualification(self):
response = self.app.get('/auctions/{}/awards'.format(self.auction_id))
self.assertEqual(response.status, '200 OK')

award_id = [i['id'] for i in response.json['data'] if i['status'] == 'pending'][0]
award = [i for i in response.json['data'] if i['status'] == 'pending.verification'][0]
award_id = award['id']
bid_token = self.initial_bids_tokens[award['bid_id']]

self.app.authorization = ('Basic', ('broker', ''))

response = self.app.post_json('/auctions/{}/awards/{}/documents?acc_token={}'.format(
self.auction_id, award_id, bid_token), {'data': {
'title': u'auction_protocol.pdf',
'url': self.generate_docservice_url(),
'hash': 'md5:' + '0' * 32,
'format': 'application/pdf',
'documentType': 'auctionProtocol',
}})
self.assertEqual(response.status, '201 Created')

response = self.app.post_json('/auctions/{}/awards/{}/documents?acc_token={}'.format(
self.auction_id, award_id, self.auction_token), {'data': {
Expand All @@ -785,23 +816,25 @@ def test_docs_disqualification(self):
self.assertEqual(response.status, '200 OK')

response = self.app.get('/auctions/{}/awards'.format(self.auction_id))
award_id2 = [i['id'] for i in response.json['data'] if i['status'] == 'pending'][0]
award = [i for i in response.json['data'] if i['status'] == 'pending.verification'][0]
award_id2 = award['id']
bid_token = self.initial_bids_tokens[award['bid_id']]

response = self.app.patch_json('/auctions/{}/awards/{}?acc_token={}'.format(
self.auction_id, award_id2, self.auction_token), {"data": {"status": "active"}})
self.assertEqual(response.status, '200 OK')

with open('docs/source/qualification/award-active-cancel.http', 'w') as self.app.file_obj:
response = self.app.patch_json('/auctions/{}/awards/{}?acc_token={}'.format(
self.auction_id, award_id2, self.auction_token), {"data": {"status": "cancelled"}})
self.assertEqual(response.status, '200 OK')
self.app.authorization = ('Basic', ('broker', ''))

response = self.app.get('/auctions/{}/awards?acc_token={}'.format(self.auction_id, self.auction_token))
award_id3 = [i['id'] for i in response.json['data'] if i['status'] == 'pending'][0]
response = self.app.post_json('/auctions/{}/awards/{}/documents?acc_token={}'.format(
self.auction_id, award_id2, bid_token), {'data': {
'title': u'auction_protocol.pdf',
'url': self.generate_docservice_url(),
'hash': 'md5:' + '0' * 32,
'format': 'application/pdf',
'documentType': 'auctionProtocol',
}})
self.assertEqual(response.status, '201 Created')

with open('docs/source/qualification/award-active-cancel-upload.http', 'w') as self.app.file_obj:
with open('docs/source/qualification/award-active-unsuccessful-upload.http', 'w') as self.app.file_obj:
response = self.app.post_json('/auctions/{}/awards/{}/documents?acc_token={}'.format(
self.auction_id, award_id3, self.auction_token), {'data': {
self.auction_id, award_id2, self.auction_token), {'data': {
'title': u'Disqualified_reason.pdf',
'url': self.generate_docservice_url(),
'hash': 'md5:' + '0' * 32,
Expand All @@ -810,9 +843,9 @@ def test_docs_disqualification(self):
}})
self.assertEqual(response.status, '201 Created')

with open('docs/source/qualification/award-active-cancel-disqualify.http', 'w') as self.app.file_obj:
with open('docs/source/qualification/award-active-disqualify.http', 'w') as self.app.file_obj:
response = self.app.patch_json('/auctions/{}/awards/{}?acc_token={}'.format(
self.auction_id, award_id3, self.auction_token), {"data": {"status": "unsuccessful", "title": "Disqualified", "description": "Candidate didn’t sign the auction protocol in 3 business days"}})
self.auction_id, award_id2, self.auction_token), {"data": {"status": "unsuccessful", "title": "Disqualified", "description": "Candidate didn’t sign the auction protocol in 3 business days"}})
self.assertEqual(response.status, '200 OK')

def _test_docs_complaints(self):
Expand Down
108 changes: 108 additions & 0 deletions docs/source/award_workflow.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
.. _award_workflow:

Award Workflow
==============

For a more detailed information see :ref:`award`

* :ref:`Qualification`
* :ref:`Confirming_qualification`
* :ref:`Candidate_disqualification`
* :ref:`Waiting_refusal`



.. graphviz::

digraph G {
subgraph cluster_1 {
node [style=filled, color=lightblue];
edge[style=dotted];
"pending.waiting" -> cancelled[label="2nd award only" fontcolor=blue];

node [style=filled, color=lightgrey];
edge[label="*" style=solid];
"pending.verification" -> unsuccessful;
edge[label="***" style=dashed];
"pending.payment" -> active;
edge[label="*****" style=filled];
"pending.waiting" -> "pending.verification"[label="2nd award only" fontcolor=blue];
label = "Awarding Process";
color=blue
}
edge[style=dashed];
"pending.payment" -> "unsuccessful";
edge[label="****" style=solid];
"pending.payment" -> unsuccessful;
edge[label=" **" style=dashed];
"pending.verification" -> "pending.payment";
edge[label="*****" style=solid];
active -> unsuccessful;

}


Legend
--------

Blue nodes represent statuses for the 2nd award ONLY

\* no protocol

\*\* protocol required

\*\*\* payment approved by the organizer

\*\*\*\* no payment approval

\*\*\*\*\* contract activation = false by the end of "signingPeriod"


Roles
-----

:Chronograph: solid

:Organizer: dashed

:Participant: dotted


Procedure Description
--------

1. The award with the highest qualifying bid initially receives a "pending.verification" status. The procedure enters the "verificationPeriod" stage, which lasts 0-3 days. Unless the protocol is uploaded and confirmed by the organizer in 3 days, the award receives an "unsuccessful" status. Otherwise, the organizer manually switches the award status to "pending.payment".
2. It is then when the qualification procedure enters the "paymentPeriod" stage, which lasts up to 11 days from the beginning of the highest bidder qualification process. When the organizer confirms that the payment has been received, the award enters the "active" status, while the procedure moves to the status "signingPeriod". This period is the same in length as the "paymentPeriod" - a maximum of 11 days from the start of qualification. If the organizer does not confirm payment by the end of the "paymentPeriod", the award automatically becomes "unsuccessful". The same is true for the signingPeriod - the organizer should upload and activate the contract in the system by the end of the "signingPeriod" in order to successfully finish the qualification procedure. Otherwise - the award will become "unsuccessful" and the qualification of the second highest qualifying bidder will begin given that he/she has not disqualified himself/herself by this time.
3. The second highest qualifying bidder, immediately after the auction ending receives the "pending.waiting" status, in which by default he/she agrees to wait for the end of the qualification of the highest qualifying bidder to be eligible to go through the qualification process if the highest bidder is disqualified. The only action that he/she can make is to manually cancel the award decision - withdraw his security deposit and lose the chance to become a winner of the auction. If he/she does that and the first highest qualifying bidder becomes "unsuccessful" at any point in the awarding process, the procedure receives the "unsuccessful" status. Provided that first award gets disqualified while the second has not disqualified himself/herself, the second award automatically changes its status from "pending.waiting" to "pending.verification", after which he/she undergoes the same qualification procedure as outlined above for the first award.

Notes
-----
1. For the bidder to be qualified and not invalidated, his/her bid should be in the amount of more or equal to the starting price of the auction + the minimal step of the auction.

1.1. In case the first two highest bids do not exceed the amount of starting price + the minimal step, the awards are not being formed at all, and the procedure automatically becomes "unsuccessful"

1.2 In case the second highest bid is smaller than the starting price + the minimal step, two awards are formed with the smaller one becoming unsuccessful immediately. The first highest bid (if larger than the starting price + minimum step) undergoes the awarding procedure and can win the auction.
2. The organizer can disqualify the award at any stage of the awarding process up until the moment, when the contract has been uploaded and activated in the system.
3. The second highest qualifying bidder can disqualify himself/herself at any point in time BEFORE the start of his/her qualification process.


Statuses
--------

:pending.waiting:
The second highest valid bidder awaits for the qualification of the first highest valid bidder. The former can choose to refuse to wait and withdraw his security deposit.

:cancelled:
Terminal status. The second highest valid bidder chose to withdraw his security deposit and not to wait for the highest valid bidder to be disqualified.

:pending.verification:
Awaiting protocol upload and confirmation by the liquidator. The highest valid bidder is able to submit the protocol as well, although it is not sufficient to move to the next status.

:pending.payment:
Awaiting payment. Organizer can change the status to active by confirming the payment has been received.

:active:
Awaiting for the contract to be signed (uploaded and activated in the system by the organizer). After the end of the "signingPeriod", the status becomes terminal.

:unsuccessful:
Terminal status. The auction was unsuccessful. Can be switched to either automatically, from any of the previous statuses or by the organizer.
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Contents:
overview
standard/index
upload
award_workflow
tutorial
fintutorial
2pc
Expand Down
Loading

0 comments on commit 4844fed

Please sign in to comment.