Commit c48364bc authored by dmitry.mashoshin's avatar dmitry.mashoshin
Browse files

Merge branch 'alesh/1087/business-day-validation-for-award-activation' into 'master'

fix(BSE/BSD awards): add validation to award activation only on business days

See merge request !1610
parents 09dd951a 03101d83
......@@ -424,3 +424,9 @@ def get_hint_date(spec, period_between_states, tender_end):
hint_date = hint_date.replace(microsecond=0)
str_hint_date = hint_date.astimezone(pytz.utc).isoformat()
return str_hint_date
def is_today_a_business_day():
kiev_tz = pytz.timezone(get_date_config()['timezone'])
now = datetime.now(kiev_tz)
return is_business_day(now)
from prozorro_sale.procedure import calculator, errors
from prozorro_sale.procedure.calculator import is_today_a_business_day
from prozorro_sale.procedure.models import base
from prozorro_sale.procedure.packages.basicSell.constants import AwardStatus
from prozorro_sale.procedure.utils import ensure_document_exist
......@@ -71,6 +72,8 @@ class AwardActive(AwardStatusUpdateMixin, awards.AwardActive):
def _can_enter_state(self, prev_status):
ensure_document_exist(self.award, ('auctionProtocol',))
if not is_today_a_business_day():
raise DataError({'status': 'Forbidden to activate not on a business day.'})
def _on_enter(self):
self._create_award_contract()
......
......@@ -24,3 +24,11 @@ def patch_clarification_doc_check(monkeypatch):
@pytest.fixture
def constants():
return basic_sell_constants
@pytest.fixture()
def patch_business_day_calculation(monkeypatch):
monkeypatch.setattr(
'prozorro_sale.procedure.packages.basicSell.awards.is_today_a_business_day',
lambda: True
)
import pytest
from freezegun import freeze_time
from prozorro_sale.procedure.errors import DataError
from prozorro_sale.procedure.errors import AclPermissionError
......@@ -16,18 +18,44 @@ def test_cancel_award(make_active_qualification_procedure, make_bid, acl_context
assert len(proc.awards) == 2 # the winner and the challenger
assert proc.awards[0].status == 'pending'
assert proc.awards[-1].status == 'pending_waiting'
assert proc.awards[0].status == constants.AwardStatus.pending
assert proc.awards[-1].status == constants.AwardStatus.pending_waiting
# cancel challenger
challenger = proc.awards[1]
if role == "bidder":
bid_id = challenger.bidId
proc.update_award_status(challenger.id, 'cancelled',
proc.update_award_status(challenger.id, constants.AwardStatus.cancelled,
acl_context(proc.bids[bid_id]))
elif role == "owner":
with pytest.raises(AclPermissionError):
proc.update_award_status(challenger.id, 'cancelled',
proc.update_award_status(challenger.id, constants.AwardStatus.cancelled,
acl_context(proc))
else:
raise Exception(f"Unsupported role {role}")
def test_activate_award_at_business_day(
make_active_qualification_procedure, make_bid, make_document, acl_context, constants
):
procedure = make_active_qualification_procedure(bids=[make_bid(status=constants.BidStatus.active, amount=3000.0)])
award = procedure.awards[0]
auction_protocol = make_document(award.Doc, document_of='award', doc_type='auctionProtocol')
procedure.create_award_document(award.id, auction_protocol, acl_context(procedure))
with freeze_time('2021-01-12 09:00:00'):
procedure.update_award_status(award.id, constants.AwardStatus.active, acl_context(procedure))
assert award.status == constants.AwardStatus.active
@pytest.mark.parametrize('current_datetime', ('2021-01-01 00:00:00', '2021-12-31 22:00:00', '2021-08-01 00:00:00'))
def test_activate_award_at_free_day(
make_active_qualification_procedure, make_bid, make_document, acl_context, constants, current_datetime
):
procedure = make_active_qualification_procedure(bids=[make_bid(status=constants.BidStatus.active, amount=3000.0)])
award = procedure.awards[0]
auction_protocol = make_document(award.Doc, document_of='award', doc_type='auctionProtocol')
procedure.create_award_document(award.id, auction_protocol, acl_context(procedure))
with freeze_time(current_datetime):
with pytest.raises(DataError, match=r'^\{"status": "Forbidden to activate not on a business day."\}$'):
procedure.update_award_status(award.id, constants.AwardStatus.active, acl_context(procedure))
assert award.status == constants.AwardStatus.pending
......@@ -11,7 +11,7 @@ def mock_auction_on_enter(monkeypatch):
monkeypatch.setattr(DutchAuction, '_on_enter', Mock())
@pytest.mark.usefixtures('mock_auction_on_enter')
@pytest.mark.usefixtures('mock_auction_on_enter', 'patch_business_day_calculation')
class TestStateMachine:
def test_initial_status(self, make_procedure):
test_procedure = make_procedure()
......
import pytest
from freezegun import freeze_time
from prozorro_sale.procedure.errors import DataError
from prozorro_sale.procedure.errors import AclPermissionError
......@@ -15,18 +17,44 @@ def test_cancel_award(make_active_qualification_procedure, make_bid, acl_context
)
assert len(proc.awards) == 2 # the winner and the challenger
assert proc.awards[0].status == 'pending'
assert proc.awards[-1].status == 'pending_waiting'
assert proc.awards[0].status == constants.AwardStatus.pending
assert proc.awards[-1].status == constants.AwardStatus.pending_waiting
# cancel challenger
challenger = proc.awards[1]
if role == "bidder":
bid_id = challenger.bidId
proc.update_award_status(challenger.id, 'cancelled',
proc.update_award_status(challenger.id, constants.AwardStatus.cancelled,
acl_context(proc.bids[bid_id]))
elif role == "owner":
with pytest.raises(AclPermissionError):
proc.update_award_status(challenger.id, 'cancelled',
proc.update_award_status(challenger.id, constants.AwardStatus.cancelled,
acl_context(proc))
else:
raise Exception(f"Unsupported role {role}")
def test_activate_award_at_business_day(
make_active_qualification_procedure, make_bid, make_document, acl_context, constants
):
procedure = make_active_qualification_procedure(bids=[make_bid(status=constants.BidStatus.active, amount=3000.0)])
award = procedure.awards[0]
auction_protocol = make_document(award.Doc, document_of='award', doc_type='auctionProtocol')
procedure.create_award_document(award.id, auction_protocol, acl_context(procedure))
with freeze_time('2021-01-12 09:00:00'):
procedure.update_award_status(award.id, constants.AwardStatus.active, acl_context(procedure))
assert award.status == constants.AwardStatus.active
@pytest.mark.parametrize('current_datetime', ('2021-01-01 00:00:00', '2021-12-31 22:00:00', '2021-08-01 00:00:00'))
def test_activate_award_at_free_day(
make_active_qualification_procedure, make_bid, make_document, acl_context, constants, current_datetime
):
procedure = make_active_qualification_procedure(bids=[make_bid(status=constants.BidStatus.active, amount=3000.0)])
award = procedure.awards[0]
auction_protocol = make_document(award.Doc, document_of='award', doc_type='auctionProtocol')
procedure.create_award_document(award.id, auction_protocol, acl_context(procedure))
with freeze_time(current_datetime):
with pytest.raises(DataError, match=r'^\{"status": "Forbidden to activate not on a business day."\}$'):
procedure.update_award_status(award.id, constants.AwardStatus.active, acl_context(procedure))
assert award.status == constants.AwardStatus.pending
......@@ -3,6 +3,7 @@ import pytest
from schematics.exceptions import DataError
@pytest.mark.usefixtures('patch_business_day_calculation')
@pytest.mark.parametrize("doc_types, expected", [
pytest.param(['rejectionProtocol'], (False, True)),
pytest.param(['act'], (False, True)),
......
......@@ -17,7 +17,7 @@ def mock_auction_on_enter(monkeypatch):
monkeypatch.setattr(EnglishAuction, '_on_enter', Mock())
@pytest.mark.usefixtures('mock_auction_on_enter')
@pytest.mark.usefixtures('mock_auction_on_enter', 'patch_business_day_calculation')
class TestStateMachine:
def test_initial_status(self, make_procedure):
test_procedure = make_procedure()
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment