Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Insider Auction models and views for auction substatuses #45

Open
wants to merge 7 commits into
base: production
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion openprocurement/auctions/insider/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
DUTCH_PERIOD = timedelta(minutes=405)
QUICK_DUTCH_PERIOD = timedelta(minutes=10)

TENDER_PERIOD_STATUSES = ['active.tendering', 'active.auction']
TENDER_PERIOD_STATUSES = [
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Я б ці підстатуси розідлив. Бо active.tendering наприклад - це статус процедури, а active.auction.dutch то є підстатус аукціону. А константа за це відповідає одна TENDER_PREIOD_STATUSES не дуже логічно.

'active.tendering', 'active.auction', 'active.auction.dutch', 'active.auction.sealedbid', 'active.auction.bestbid'
]
NUMBER_OF_STAGES = 80 # from openprocurement.auction.insider.constants import DUTCH_ROUNDS as NUMBER_OF_STAGES
DUTCH_TIMEDELTA = timedelta(minutes=405) # from openprocurement.auction.insider.constants import DUTCH_TIMEDELTA
STAGE_TIMEDELTA = DUTCH_TIMEDELTA / NUMBER_OF_STAGES
Expand Down
10 changes: 9 additions & 1 deletion openprocurement/auctions/insider/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from openprocurement.api.utils import calculate_business_date
from openprocurement.api.models import get_now, Value, Period, TZ, SANDBOX_MODE
from openprocurement.auctions.core.models import IAuction
from openprocurement.auctions.flash.models import COMPLAINT_STAND_STILL_TIME, auction_view_role
from openprocurement.auctions.flash.models import COMPLAINT_STAND_STILL_TIME, auction_role, auction_view_role
from openprocurement.auctions.dgf.models import (
DGFFinancialAssets as BaseAuction,
get_auction, Bid as BaseBid,
Expand Down Expand Up @@ -98,6 +98,10 @@ class Auction(BaseAuction):

class Options:
roles = {
'auction_patch': whitelist('auctionUrl', 'bids', 'lots', 'status'),
'active.auction.dutch': auction_role,
'active.auction.sealedbid': auction_role,
'active.auction.bestbid': auction_role,
'auction_view': auction_view_role,
'edit_active.tendering': edit_role,
'Administrator': Administrator_role,
Expand All @@ -108,6 +112,10 @@ class Options:
auctionPeriod = ModelType(AuctionAuctionPeriod, required=True, default={})
auctionParameters = ModelType(AuctionParameters)
minimalStep = ModelType(Value)
status = StringType(choices=['draft', 'pending.verification', 'invalid', 'active.tendering', 'active.auction',
'active.auction.dutch', 'active.auction.sealedbid', 'active.auction.bestbid',
'active.qualification', 'active.awarded', 'complete', 'cancelled', 'unsuccessful'],
default='active.tendering')

def initialize(self):
if not self.enquiryPeriod:
Expand Down
2 changes: 1 addition & 1 deletion openprocurement/auctions/insider/tests/award.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def test_create_auction_award_invalid(self):
self.assertEqual(response.content_type, 'application/json')
self.assertEqual(response.json['status'], 'error')
self.assertEqual(response.json['errors'], [
{u'description': u'No JSON object could be decoded',
{u'description': u'Expecting value: line 1 column 1 (char 0)',
u'location': u'body', u'name': u'data'}
])

Expand Down
2 changes: 1 addition & 1 deletion openprocurement/auctions/insider/tests/bidder.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def test_create_auction_bidder_invalid(self):
self.assertEqual(response.content_type, 'application/json')
self.assertEqual(response.json['status'], 'error')
self.assertEqual(response.json['errors'], [
{u'description': u'No JSON object could be decoded',
{u'description': u'Expecting value: line 1 column 1 (char 0)',
u'location': u'body', u'name': u'data'}
])

Expand Down
2 changes: 1 addition & 1 deletion openprocurement/auctions/insider/tests/cancellation.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def test_create_auction_cancellation_invalid(self):
self.assertEqual(response.content_type, 'application/json')
self.assertEqual(response.json['status'], 'error')
self.assertEqual(response.json['errors'], [
{u'description': u'No JSON object could be decoded',
{u'description': u'Expecting value: line 1 column 1 (char 0)',
u'location': u'body', u'name': u'data'}
])

Expand Down
2 changes: 1 addition & 1 deletion openprocurement/auctions/insider/tests/complaint.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def test_create_auction_complaint_invalid(self):
self.assertEqual(response.content_type, 'application/json')
self.assertEqual(response.json['status'], 'error')
self.assertEqual(response.json['errors'], [
{u'description': u'No JSON object could be decoded',
{u'description': u'Expecting value: line 1 column 1 (char 0)',
u'location': u'body', u'name': u'data'}
])

Expand Down
2 changes: 1 addition & 1 deletion openprocurement/auctions/insider/tests/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def test_create_auction_contract_invalid(self):
self.assertEqual(response.content_type, 'application/json')
self.assertEqual(response.json['status'], 'error')
self.assertEqual(response.json['errors'], [
{u'description': u'No JSON object could be decoded',
{u'description': u'Expecting value: line 1 column 1 (char 0)',
u'location': u'body', u'name': u'data'}
])

Expand Down
2 changes: 1 addition & 1 deletion openprocurement/auctions/insider/tests/question.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def test_create_auction_question_invalid(self):
self.assertEqual(response.content_type, 'application/json')
self.assertEqual(response.json['status'], 'error')
self.assertEqual(response.json['errors'], [
{u'description': u'No JSON object could be decoded',
{u'description': u'Expecting value: line 1 column 1 (char 0)',
u'location': u'body', u'name': u'data'}
])

Expand Down
4 changes: 2 additions & 2 deletions openprocurement/auctions/insider/tests/tender.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ def test_create_auction_invalid(self):
self.assertEqual(response.content_type, 'application/json')
self.assertEqual(response.json['status'], 'error')
self.assertEqual(response.json['errors'], [
{u'description': u'No JSON object could be decoded',
{u'description': u'Expecting value: line 1 column 1 (char 0)',
u'location': u'body', u'name': u'data'}
])

Expand Down Expand Up @@ -610,7 +610,7 @@ def test_create_auction_generated(self):
u'tenderPeriod', u'minimalStep', u'items', u'value', u'procuringEntity', u'next_check', u'dgfID',
u'procurementMethod', u'awardCriteria', u'submissionMethod', u'title', u'owner', u'auctionPeriod',
u'eligibilityCriteria', u'eligibilityCriteria_en', u'eligibilityCriteria_ru', 'documents',
u'dgfDecisionDate', u'dgfDecisionID', u'tenderAttempts'
u'dgfDecisionDate', u'dgfDecisionID', u'tenderAttempts',
]))
self.assertNotEqual(data['id'], auction['id'])
self.assertNotEqual(data['doc_id'], auction['id'])
Expand Down
2 changes: 1 addition & 1 deletion openprocurement/auctions/insider/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
def validate_auction_auction_data(request):
data = validate_patch_auction_data(request)
auction = request.validated['auction']
if auction.status != 'active.auction':
if auction.status not in ['active.auction', 'active.auction.dutch', 'active.auction.sealedbid', 'active.auction.bestbid']:
request.errors.add('body', 'data', 'Can\'t {} in current ({}) auction status'.format('report auction results' if request.method == 'POST' else 'update auction urls', auction.status))
request.errors.status = 403
return
Expand Down
15 changes: 9 additions & 6 deletions openprocurement/auctions/insider/views/auction.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@
save_auction,
apply_patch,
opresource,
remove_draft_bids
)
from openprocurement.auctions.insider.validation import (
validate_auction_auction_data,
remove_draft_bids,
)
from openprocurement.auctions.dgf.views.financial.auction import (
FinancialAuctionAuctionResource,
)
from openprocurement.auctions.insider.utils import create_awards, invalidate_empty_bids, merge_auction_results
from openprocurement.auctions.insider.utils import (
create_awards,
invalidate_empty_bids,
merge_auction_results,
)
from openprocurement.auctions.insider.validation import validate_auction_auction_data
from openprocurement.auctions.insider.constants import TENDER_PERIOD_STATUSES


@opresource(name='dgfInsider:Auction Auction',
Expand All @@ -27,7 +30,7 @@ class InsiderAuctionAuctionResource(FinancialAuctionAuctionResource):

@json_view(permission='auction')
def collection_get(self):
if self.request.validated['auction_status'] not in ['active.tendering', 'active.auction']:
if self.request.validated['auction_status'] not in TENDER_PERIOD_STATUSES:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Хіба тобі потрібна константа TENDER_PERIOD_STATUSES, коли в тебе є https://github.com/openprocurement/openprocurement.auctions.core/pull/27/files#diff-5553f0cc7a26748bc279b460b148bef5R6

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yarsanich, не знав як логічно виділити групу статусів, за яких в кожній з процедур можна робити GET запити до аукціону.

self.request.errors.add('body', 'data', 'Can\'t get auction info in current ({}) auction status'.format(
self.request.validated['auction_status']))
self.request.errors.status = 403
Expand Down
102 changes: 93 additions & 9 deletions openprocurement/auctions/insider/views/bid.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
# -*- coding: utf-8 -*-

from openprocurement.api.models import get_now
from openprocurement.api.utils import (
json_view,
context_unpack,
set_ownership,
)
from openprocurement.auctions.core.utils import (
opresource,
apply_patch,
save_auction,
)
from openprocurement.auctions.core.validation import (
validate_bid_data,
validate_patch_bid_data,
)
from openprocurement.auctions.dgf.views.financial.bid import (
FinancialAuctionBidResource,
)

from openprocurement.api.utils import (
json_view,
context_unpack,
set_ownership
)
from openprocurement.api.models import get_now
from openprocurement.auctions.core.validation import validate_bid_data, validate_patch_bid_data
from openprocurement.auctions.insider.constants import TENDER_PERIOD_STATUSES


@opresource(name='dgfInsider:Auction Bids',
collection_path='/auctions/{auction_id}/bids',
path='/auctions/{auction_id}/bids/{bid_id}',
Expand Down Expand Up @@ -138,6 +140,88 @@ def collection_post(self):
}
}

@json_view(permission='view_auction')
def collection_get(self):
"""Bids Listing

Get Bids List
-------------

Example request to get bids list:

.. sourcecode:: http

GET /auctions/4879d3f8ee2443169b5fbbc9f89fa607/bids HTTP/1.1
Host: example.com
Accept: application/json

This is what one should expect in response:

.. sourcecode:: http

HTTP/1.1 200 OK
Content-Type: application/json

{
"data": [
{
"value": {
"amount": 489,
"currency": "UAH",
"valueAddedTaxIncluded": true
}
}
]
}

"""
auction = self.request.validated['auction']
if self.request.validated['auction_status'] in TENDER_PERIOD_STATUSES:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Знову ж таки валідатор.

self.request.errors.add('body', 'data', 'Can\'t view bids in current ({}) auction status'.format(
self.request.validated['auction_status']))
self.request.errors.status = 403
return
return {'data': [i.serialize(self.request.validated['auction_status']) for i in auction.bids]}

@json_view(permission='view_auction')
def get(self):
"""Retrieving the proposal

Example request for retrieving the proposal:

.. sourcecode:: http

GET /auctions/4879d3f8ee2443169b5fbbc9f89fa607/bids/71b6c23ed8944d688e92a31ec8c3f61a HTTP/1.1
Host: example.com
Accept: application/json

And here is the response to be expected:

.. sourcecode:: http

HTTP/1.0 200 OK
Content-Type: application/json

{
"data": {
"value": {
"amount": 600,
"currency": "UAH",
"valueAddedTaxIncluded": true
}
}
}

"""
if self.request.authenticated_role == 'bid_owner':
return {'data': self.request.context.serialize('view')}
if self.request.validated['auction_status'] in TENDER_PERIOD_STATUSES:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Винеси у валідатор.

self.request.errors.add('body', 'data', 'Can\'t view bid in current ({}) auction status'.format(
self.request.validated['auction_status']))
self.request.errors.status = 403
return
return {'data': self.request.context.serialize(self.request.validated['auction_status'])}

@json_view(content_type="application/json", permission='edit_bid', validators=(validate_patch_bid_data,))
def patch(self):
"""Update of proposal
Expand Down
2 changes: 1 addition & 1 deletion openprocurement/auctions/insider/views/bid_document.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class InsiderAuctionBidDocumentResource(FinancialAuctionBidDocumentResource):

def validate_bid_document(self, operation):
auction = self.request.validated['auction']
if auction.status not in ['active.tendering', 'active.auction', 'active.qualification']:
if self.request.validated['auction_status'] not in TENDER_PERIOD_STATUSES and self.request.validated['auction_status'] != 'active.qualification':
self.request.errors.add('body', 'data', 'Can\'t {} document in current ({}) auction status'.format(operation, auction.status))
self.request.errors.status = 403
return
Expand Down
29 changes: 28 additions & 1 deletion openprocurement/auctions/insider/views/cancellation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

from openprocurement.auctions.core.utils import (
opresource,
add_next_award,
)
from openprocurement.auctions.dgf.views.financial.cancellation import (
FinancialAuctionCancellationResource,
)
from openprocurement.auctions.insider.constants import TENDER_PERIOD_STATUSES


@opresource(name='dgfInsider:Auction Cancellations',
Expand All @@ -14,4 +16,29 @@
auctionsprocurementMethodType="dgfInsider",
description="Insider auction cancellations")
class InsiderAuctionCancellationResource(FinancialAuctionCancellationResource):
pass

def cancel_auction(self):
auction = self.request.validated['auction']
if auction.status in TENDER_PERIOD_STATUSES:
auction.bids = []
auction.status = 'cancelled'

def cancel_lot(self, cancellation=None):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Що змінилося у cancel_lot?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if auction.status == 'active.auction' and all ... > if 'active.auction' in auction.status and all ...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Пропоную то все винести в окрему функцію, або у валідатор і змінити самі views з використанням валідатору, щоб не копіювати кучу коду, якщо таке можливо звичайно.


if not cancellation:
cancellation = self.context
auction = self.request.validated['auction']
[setattr(i, 'status', 'cancelled') for i in auction.lots if i.id == cancellation.relatedLot]
statuses = set([lot.status for lot in auction.lots])
if statuses == set(['cancelled']):
self.cancel_auction()
elif not statuses.difference(set(['unsuccessful', 'cancelled'])):
auction.status = 'unsuccessful'
elif not statuses.difference(set(['complete', 'unsuccessful', 'cancelled'])):
auction.status = 'complete'
if 'active.auction' in auction.status and all([
i.auctionPeriod and i.auctionPeriod.endDate
for i in self.request.validated['auction'].lots
if i.numberOfBids > 1 and i.status == 'active'
]):
add_next_award(self.request)
Loading