Skip to content

Commit

Permalink
Merge cdf7fe2 into fa4270c
Browse files Browse the repository at this point in the history
  • Loading branch information
oleksiyVeretiuk committed Apr 19, 2018
2 parents fa4270c + cdf7fe2 commit a8b9425
Show file tree
Hide file tree
Showing 24 changed files with 1,749 additions and 1,175 deletions.
74 changes: 67 additions & 7 deletions openregistry/lots/loki/constants.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,87 @@
# -*- coding: utf-8 -*-
from datetime import timedelta

LOT_STATUSES = ["draft", "pending", "deleted"]
LOT_STATUSES = [
"draft", "composing", "pending", "deleted", "active.salable", "active.awaiting", "active.auction",
"active.contracting", "pending.sold", "pending.dissolution", "sold", "dissolved", "invalid"]

STATUS_CHANGES = {
"draft": {
"editing_permissions": ["lot_owner", "Administrator"],
"next_status": {
"pending": ["lot_owner", "Administrator"],
"composing": ["lot_owner", "Administrator"],
}
},
"composing": {
"editing_permissions": ["concierge"],
"next_status": {
"pending": ["concierge"],
"invalid": ["concierge"],
}
},
"pending": {
"editing_permissions": ["lot_owner", "Administrator"],
"next_status": {
"deleted": ["lot_owner", "Administrator"]
"deleted": ["lot_owner", "Administrator"],
"active.salable": ["lot_owner", "Administrator"],
}
},
"deleted": {
"editing_permissions": [],
"next_status": {}
},
"active.salable": {
"editing_permissions": ["Administrator", "convoy"],
"next_status": {
"active.awaiting": ["Administrator", "convoy"]
}
},
"active.awaiting": {
"editing_permissions": ["Administrator", "convoy"],
"next_status": {
"active.salable": ["Administrator", "convoy"],
"active.auction": ["Administrator", "convoy"]
}
},
"active.auction": {
"editing_permissions": ["Administrator", "convoy"],
"next_status": {
"active.contracting": ["Administrator", "convoy"],
"pending.dissolution": ["Administrator", "convoy"]
}
},
"active.contracting": {
"editing_permissions": ["Administrator", "convoy"],
"next_status": {
"pending.sold": ["Administrator", "convoy"],
"pending.dissolution": ["Administrator", "convoy"]
}
},
"pending.sold": {
"editing_permissions": ["Administrator", "concierge"],
"next_status": {
"sold": ["Administrator", "concierge"]
}
},
"pending.dissolution": {
"editing_permissions": ["Administrator", "concierge"],
"next_status": {
"dissolved": ["Administrator", "concierge"]
}
},
"sold": {
"editing_permissions": [],
"next_status": {}
},
"dissolved": {
"editing_permissions": [],
"next_status": {}
},
"invalid": {
"editing_permissions": [],
"next_status": {}
},
}

DOCUMENT_TYPES = [
'notice', 'technicalSpecifications', 'illustration', 'virtualDataRoom',
'x_presentation', 'x_dgfAssetFamiliarization', 'procurementPlan', 'projectPlan'
]
RECTIFICATION_PERIOD_DURATION = timedelta(days=1)
DEFAULT_DUTCH_STEPS = 99
15 changes: 0 additions & 15 deletions openregistry/lots/loki/events.py

This file was deleted.

226 changes: 104 additions & 122 deletions openregistry/lots/loki/models.py
Original file line number Diff line number Diff line change
@@ -1,178 +1,160 @@
# -*- coding: utf-8 -*-
from uuid import uuid4

from copy import deepcopy
from pyramid.security import Allow
from schematics.exceptions import ValidationError
from schematics.transforms import blacklist
from schematics.types import StringType, IntType, URLType
from schematics.types import StringType, IntType, MD5Type
from schematics.types.compound import ModelType, ListType
from schematics.types.serializable import serializable
from zope.interface import implementer


from openregistry.lots.core.models import (
Value,
Guarantee,
Period,
BaseIdentifier,
BaseDocument,
Address,
ContactPoint,
BaseItem,
BaseUnit,
Organization,
ItemClassification,
Classification,
LokiDocument as Document,
LokiItem as Item,
Decision,
AssetCustodian,
AssetHolder,
Model,
IsoDateTimeType,
IsoDurationType,
DecimalType,
IDENTIFIER_CODES,
ILot,
Lot as BaseLot
Guarantee,
Period,
Value

)

from .constants import LOT_STATUSES, DOCUMENT_TYPES
from openregistry.lots.core.models import ILot, Lot as BaseLot
from openregistry.lots.core.utils import (
get_now,
calculate_business_date
)

from .constants import (
LOT_STATUSES,
RECTIFICATION_PERIOD_DURATION,
)
from .roles import (
item_roles,
publication_roles,
lot_roles
lot_roles,
)


class ILokiLot(ILot):
""" Marker interface for basic lots """


class Document(BaseDocument):
documentOf = StringType(choices=['lot', 'item'])
documentType = StringType(choices=DOCUMENT_TYPES)
dateSigned = IsoDateTimeType()
index = IntType(required=False)
accessDetails = StringType()

def validate_accessDetails(self, data, value):
if value is None and data['documentType'] == 'x_dgfAssetFamiliarization':
raise ValidationError(u"accessDetails is required, when documentType is x_dgfAssetFamiliarization")

def validate_dateSigned(self, data, value):
if value is None and data['documentType'] in ['procurementPlan', 'projectPlan']:
raise ValidationError(u"dateSigned is required, when documentType is procurementPlan or projectPlan")


class RegistrationDetails(Model):
status = StringType(choices=['unknown', 'proceed', 'complete'], required=True)
registrationID = StringType()
registrationDate = IsoDateTimeType()

def validate_registrationID(self, data, value):
if value and data['status'] != 'complete':
raise ValidationError(u"You can fill registrationID only when status is complete")

def validate_registrationDate(self, data, value):
if value and data['status'] != 'complete':
raise ValidationError(u"You can fill registrationDate only when status is complete")


class Item(BaseItem):
class Options:
roles = item_roles

unit = ModelType(BaseUnit, required=True)
quantity = DecimalType(precision=-4, required=True)
address = ModelType(Address, required=True)
classification = ModelType(ItemClassification, required=True)
registrationDetails = ModelType(RegistrationDetails, required=True)


class Identifier(BaseIdentifier):
scheme = StringType(choices=IDENTIFIER_CODES)
legalName = StringType(required=True)
uri = URLType(required=True)


class LotCustodian(Organization):
name = StringType()
identifier = ModelType(Identifier, serialize_when_none=False)
additionalIdentifiers = ListType(ModelType(Identifier), default=list())
address = ModelType(Address, serialize_when_none=False)
contactPoint = ModelType(ContactPoint, serialize_when_none=False)
kind = StringType(choices=['general', 'special', 'other'])


class LotHolder(Model):
name = StringType(required=True)
name_ru = StringType()
name_en = StringType()
identifier = ModelType(Identifier, required=True)
additionalIdentifiers = ListType(ModelType(Identifier), default=list())
address = ModelType(Address)
contactPoint = ModelType(ContactPoint)


class StartDateRequiredPeriod(Period):
startDate = IsoDateTimeType(required=True)


class UAEDRAndMFOClassification(Classification):
scheme = StringType(choices=['UA-EDR', 'MFO'], required=True)


class AccountDetails(Model):
description = StringType()
bankName = StringType()
accountNumber = StringType()
additionalClassifications = ListType(ModelType(Classification), default=list())
additionalClassifications = ListType(ModelType(UAEDRAndMFOClassification), default=list())


class AuctionParameters(Model):
type = StringType(choices=['english', 'insider'])
dutchSteps = IntType(default=None, min_value=1, max_value=100)


class Auction(Model):
id = StringType()
auctionID = StringType()
procurementMethodType = StringType(choices=['Loki.english', 'Loki.insider'])
auctionPeriod = ModelType(StartDateRequiredPeriod, required=True)
tenderingDuration = IsoDurationType(required=True)
tenderingDuration = IsoDurationType()
documents = ListType(ModelType(Document))
value = ModelType(Value, required=True)
minimalStep = ModelType(Value, required=True)
guarantee = ModelType(Guarantee, required=True)
registrationFee = ModelType(Guarantee)
accountDetails = ModelType(AccountDetails)
dutchSteps = IntType(default=None, min_value=1, max_value=100)

def validate_dutchSteps(self, data, value):
if value and data['procurementMethodType'] != 'Loki.insider':
raise ValidationError('Field dutchSteps is allowed only when procuremenentMethodType is Loki.insider')
if data['procurementMethodType'] == 'Loki.insider' and not value:
data['dutchSteps'] = 99 if data.get('dutchSteps') is None else data['dutchSteps']


class DecisionDetails(Model):
title = StringType()
decisionDate = IsoDateTimeType(required=True)
decisionID = StringType(required=True)


class Publication(Model):
class Options:
roles = publication_roles

id = StringType(required=True, min_length=1, default=lambda: uuid4().hex)
documents = ListType(ModelType(Document), default=list())
auctions = ListType(ModelType(Auction), max_size=3, min_size=3)
decisionDetails = ModelType(DecisionDetails, required=True)

def validate_auctions(self, data, value):
pass
auctionParameters = ModelType(AuctionParameters)


@implementer(ILokiLot)
class Lot(BaseLot):
class Options:
roles = lot_roles

title = StringType()
status = StringType(choices=LOT_STATUSES, default='draft')
description = StringType(required=True)
description = StringType()
lotType = StringType(default="loki")
lotCustodian = ModelType(LotCustodian, serialize_when_none=False)
lotHolder = ModelType(LotHolder, serialize_when_none=False)
officialRegistrationID = StringType(serialize_when_none=True)
rectificationPeriod = ModelType(Period)
lotCustodian = ModelType(AssetCustodian, serialize_when_none=False)
lotHolder = ModelType(AssetHolder, serialize_when_none=False)
officialRegistrationID = StringType(serialize_when_none=False)
items = ListType(ModelType(Item), default=list())
publications = ListType(ModelType(Publication), default=list())
documents = ListType(ModelType(Document), default=list())
decisionDetails = ModelType(DecisionDetails, required=True)
decisions = ListType(ModelType(Decision), default=list(), min_size=1, max_size=2, required=True)
assets = ListType(MD5Type(), required=True, min_size=1, max_size=1)
auctions = ListType(ModelType(Auction), max_size=3, min_size=3, required=True)

def get_role(self):
root = self.__parent__
request = root.request
if request.authenticated_role == 'Administrator':
role = 'Administrator'
elif request.authenticated_role == 'concierge':
role = 'concierge'
elif request.authenticated_role == 'convoy':
role = 'convoy'
else:
after_rectificationPeriod = bool(
request.context.rectificationPeriod and
request.context.rectificationPeriod.endDate < get_now()
)
if request.context.status == 'pending' and after_rectificationPeriod:
return 'edit_pendingAfterRectificationPeriod'
role = 'edit_{}'.format(request.context.status)
return role

def validate_auctions(self, data, value):
if not value:
return

# Use the first two auction because they must be english auctions
# because of strict order(english, english, insider)
for auction in value[:2]:
if auction.auctionParameters and auction.auctionParameters.dutchSteps:
raise ValidationError('dutchSteps can be filled only when procurementMethodType is Loki.insider.')

if value[0].tenderingDuration:
raise ValidationError('First loki.english have no tenderingDuration.')
if not all(auction.tenderingDuration for auction in value[1:]):
raise ValidationError('tenderingDuration is required for second loki.english and loki.insider.')
if value[1].tenderingDuration != value[2].tenderingDuration:
raise ValidationError('tenderingDuration for second loki.english and loki.insider should be the same.')

@serializable(serialized_name='auctions', serialize_when_none=False)
def serialize_auctions(self):
self.auctions[0]['procurementMethodType'] = 'Loki.english'
self.auctions[1]['procurementMethodType'] = 'Loki.english'
self.auctions[2]['procurementMethodType'] = 'Loki.insider'

auto_calculated_fields = ['value', 'minimalStep', 'registrationFee', 'guarantee']
for i in range(1, 3):
for key in auto_calculated_fields:
object_class = self.auctions[0][key].__class__
self.auctions[i][key] = object_class(self.auctions[0][key].serialize())
self.auctions[i][key]['amount'] = self.auctions[0][key]['amount'] / 2


@serializable(serialized_name='rectificationPeriod', serialize_when_none=False)
def serialize_rectificationPeriod(self):
if self.status == 'pending' and not self.rectificationPeriod:
self.rectificationPeriod = type(self).rectificationPeriod.model_class()
self.rectificationPeriod.startDate = get_now()
self.rectificationPeriod.endDate = calculate_business_date(self.rectificationPeriod.startDate,
RECTIFICATION_PERIOD_DURATION)

def __acl__(self):
acl = [
Expand Down
Loading

0 comments on commit a8b9425

Please sign in to comment.