Skip to content

Commit

Permalink
Feature: Lot exposition minimal period validation (#22)
Browse files Browse the repository at this point in the history
* feature
* lot validation
* request fixes
* test auction cleanup
* small fix
* description fix
* PEP8 fixes, code standart fixes, travis fix
* PEP8 small fix, line added
* acceleration_test fix, chronograph test fix
* docs_acceleration_test fix, chronograph test fix
* ukrainian translation for latest changes added
  • Loading branch information
antonkorobko authored and leits committed Sep 6, 2017
1 parent c7273ae commit 2b79396
Show file tree
Hide file tree
Showing 12 changed files with 165 additions and 4 deletions.
10 changes: 9 additions & 1 deletion docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ def test_docs_acceleration(self):
data['submissionMethodDetails'] = 'quick'
data['mode'] = 'test'
data["auctionPeriod"] = {
"startDate": (now + timedelta(minutes=5)).isoformat()
"startDate": (now + timedelta(days=12)).isoformat()
}
with open('docs/source/tutorial/auction-post-acceleration.http', 'w') as self.app.file_obj:
response = self.app.post_json(
Expand Down Expand Up @@ -316,6 +316,14 @@ def test_docs_tutorial(self):
auction = response.json['data']
owner_token = response.json['access']['token']

data = test_auction_data.copy()
data["auctionPeriod"] = {
"startDate": (now + timedelta(days=6)).isoformat()
}
with open('docs/source/tutorial/tenderperiod-validation-error.http', 'w') as self.app.file_obj:
response = self.app.post_json('/auctions?opt_pretty=1', {"data": data}, status=422)
self.assertEqual(response.status, '422 Unprocessable Entity')

with open('docs/source/tutorial/blank-auction-view.http', 'w') as self.app.file_obj:
response = self.app.get('/auctions/{}'.format(auction['id']))
self.assertEqual(response.status, '200 OK')
Expand Down
Binary file modified docs/source/locale/uk/LC_MESSAGES/overview.mo
Binary file not shown.
3 changes: 3 additions & 0 deletions docs/source/locale/uk/LC_MESSAGES/overview.po
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ msgstr ""
"статусу *active.enquiries*), оскільки він накладається на період прийому "
"пропозицій *active.tendering*."

msgid "`tenderPeriod` must be at least 7 calendar days."
msgstr "`tenderPeriod` має складати щонайменше 7 календарних днів."

msgid "Procedure can be switched from *draft* status to *active.tendering*."
msgstr "Процедура переходить зі статусу *draft* до *active.tendering*."

Expand Down
Binary file modified docs/source/locale/uk/LC_MESSAGES/tutorial.mo
Binary file not shown.
22 changes: 22 additions & 0 deletions docs/source/locale/uk/LC_MESSAGES/tutorial.po
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,28 @@ msgstr ""
"модифікувався. Зверніть увагу на `procurementMethodType`, а також на те, що "
"аукціон створюється зі статусом `active.tendering`."

msgid ""
"Keep in mind that `tenderPeriod` must be at least 7 calendar days."
msgstr ""
"Пам'ятайте, що `tenderPeriod` має складати щонайменше 7 календарних "
"днів."

msgid ""
"When `auctionPeriod.startDate` has an incorrect date, 422 Unprocessable Entity "
"error is raised and \"tenderPeriod should be greater than 6 days\" message is "
"returned in JSON response."
msgstr ""
"Якщо у `auctionPeriod.startDate` передано некоректну дату, генерується помилка `422` "
"`Unprocessable Entity` та у JSON відповіді повертається повідомлення "
"\"tenderPeriod should be greater than 6 days\"."

msgid ""
"Let's set `auctionPeriod.startDate` to `now + timedelta(days=6)` and ValidationError "
"will be returned:"
msgstr ""
"Встановимо `auctionPeriod.startDate` у значення `now + timedelta(days=6)` - у відповідь "
"ми отримаємо помилку `ValidationError`."

msgid "And one can retrieve the question list:"
msgstr "Можна отримати список запитань:"

Expand Down
1 change: 1 addition & 0 deletions docs/source/overview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Features
--------

* No need to specify enquiries period (there is no *active.enquiries* status), since it overlaps with *active.tendering* period.
* `tenderPeriod` must be at least 7 calendar days.
* Procedure can be switched from *draft* status to *active.tendering*.
* During *active.tendering* period participants can ask questions, submit proposals, and upload documents.
* The only date Organizer has to provide is *Tender.auctionPeriod.startDate*, the rest will be calculated automatically.
Expand Down
12 changes: 12 additions & 0 deletions docs/source/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,18 @@ body of response reveals the information about the created auction: its internal
modified. Pay attention to the `procurementMethodType`. Note that auction is
created with `active.tendering` status.

Keep in mind that `tenderPeriod` must be at least 7 calendar days.

When `auctionPeriod.startDate` has an incorrect date, 422 Unprocessable Entity
error is raised and "tenderPeriod should be greater than 6 days" message is
returned in JSON response.

Let's set `auctionPeriod.startDate` to `now + timedelta(days=6)` and ValidationError
will be returned:

.. include:: tutorial/tenderperiod-validation-error.http
:code:

Let's access the URL of the created object (the `Location` header of the response):

.. include:: tutorial/blank-auction-view.http
Expand Down
88 changes: 88 additions & 0 deletions docs/source/tutorial/tenderperiod-validation-error.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
POST /api/2.3/auctions?opt_pretty=1 HTTP/1.0
Authorization: Basic YnJva2VyOg==
Content-Length: 2037
Content-Type: application/json
Host: api-sandbox.ea.openprocurement.org

{
"data": {
"title": "футляри до державних нагород",
"minimalStep": {
"currency": "UAH",
"amount": 35
},
"auctionPeriod": {
"startDate": "2017-09-02T15:56:30.045984"
},
"tenderAttempts": 1,
"procurementMethodType": "dgfOtherAssets",
"value": {
"currency": "UAH",
"amount": 100
},
"dgfID": "219560",
"items": [
{
"description": "Земля для військовослужбовців",
"classification": {
"scheme": "CPV",
"id": "66113000-5",
"description": "Земельні ділянки"
},
"address": {
"countryName": "Україна",
"postalCode": "79000",
"region": "м. Київ",
"streetAddress": "вул. Банкова 1",
"locality": "м. Київ"
},
"contractPeriod": {
"startDate": "2017-08-30T15:56:30.043635",
"endDate": "2017-09-02T15:56:30.043635"
},
"unit": {
"code": "44617100-9",
"name": "item"
},
"quantity": 5.001
}
],
"procuringEntity": {
"contactPoint": {
"name": "Державне управління справами",
"telephone": "0440000000"
},
"identifier": {
"scheme": "UA-EDR",
"id": "00037256",
"uri": "http://www.dus.gov.ua/"
},
"name": "Державне управління справами",
"address": {
"countryName": "Україна",
"postalCode": "01220",
"region": "м. Київ",
"streetAddress": "вул. Банкова, 11, корпус 1",
"locality": "м. Київ"
}
}
}
}


422 Unprocessable Entity
Content-Type: application/json; charset=UTF-8

{
"status": "error",
"errors": [
{
"description": [
"tenderPeriod should be greater than 6 days"
],
"location": "body",
"name": "tenderPeriod"
}
]
}

7 changes: 7 additions & 0 deletions openprocurement/auctions/dgf/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
from datetime import datetime, timedelta, time, date
from openprocurement.api.models import (TZ)


MINIMAL_EXPOSITION_PERIOD = timedelta(days=7)
MINIMAL_EXPOSITION_REQUIRED_FROM = datetime(2017, 8, 10, tzinfo=TZ)
9 changes: 8 additions & 1 deletion openprocurement/auctions/dgf/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
ProcuringEntity as BaseProcuringEntity, Question as BaseQuestion,
get_auction, Administrator_role
)
from openprocurement.auctions.dgf.constants import (MINIMAL_EXPOSITION_PERIOD, MINIMAL_EXPOSITION_REQUIRED_FROM)


def read_json(name):
Expand Down Expand Up @@ -293,7 +294,13 @@ def initialize(self):
lot.date = now

def validate_tenderPeriod(self, data, period):
pass
"""Auction start date must be not closer than MINIMAL_EXPOSITION_PERIOD days and not a holiday"""
if not (period and period.startDate and period.endDate):
return
if (data.get('revisions')[0].date if data.get('revisions') else get_now()) < MINIMAL_EXPOSITION_REQUIRED_FROM:
return
if calculate_business_date(period.startDate, MINIMAL_EXPOSITION_PERIOD) > period.endDate:
raise ValidationError(u"tenderPeriod should be greater than 6 days")

def validate_value(self, data, value):
if value.currency != u'UAH':
Expand Down
5 changes: 3 additions & 2 deletions openprocurement/auctions/dgf/tests/chronograph.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,11 @@ def test_reset_auction_period(self):
auction = self.db.get(self.auction_id)
self.assertGreater(auction['next_check'], response.json['data']['tenderPeriod']['endDate'])
auction['tenderPeriod']['endDate'] = auction['tenderPeriod']['startDate']
auction['tenderPeriod']['startDate'] = (datetime.strptime(auction['tenderPeriod']['endDate'][:19], "%Y-%m-%dT%H:%M:%S") - timedelta(days=10)).isoformat()
if self.initial_lots:
auction['lots'][0]['auctionPeriod']['startDate'] = auction['tenderPeriod']['startDate']
auction['lots'][0]['auctionPeriod']['startDate'] = auction['tenderPeriod']['endDate']
else:
auction['auctionPeriod']['startDate'] = auction['tenderPeriod']['startDate']
auction['auctionPeriod']['startDate'] = auction['tenderPeriod']['endDate']
self.db.save(auction)

response = self.app.patch_json('/auctions/{}'.format(self.auction_id), {'data': {'id': self.auction_id}})
Expand Down
12 changes: 12 additions & 0 deletions openprocurement/auctions/dgf/tests/tender.py
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,18 @@ def test_create_auction_invalid(self):
{u'description': [u'period should begin after auctionPeriod'], u'location': u'body', u'name': u'awardPeriod'}
])

# Lot exposition time period validation
data = self.initial_data['auctionPeriod']
self.initial_data['auctionPeriod'] = {'startDate': (now + timedelta(days=5)).isoformat()}
response = self.app.post_json(request_path, {'data': self.initial_data}, status=422)
self.initial_data['auctionPeriod'] = data
self.assertEqual(response.status, '422 Unprocessable Entity')
self.assertEqual(response.content_type, 'application/json')
self.assertEqual(response.json['status'], 'error')
self.assertEqual(response.json['errors'], [
{u'description': [u'tenderPeriod should be greater than 6 days'], u'location': u'body', u'name': u'tenderPeriod'}
])

data = self.initial_data['minimalStep']
self.initial_data['minimalStep'] = {'amount': '1000.0'}
response = self.app.post_json(request_path, {'data': self.initial_data}, status=422)
Expand Down

0 comments on commit 2b79396

Please sign in to comment.