diff --git a/ereuse_devicehub/migrations/versions/3a3601ac8224_tradedocuments.py b/ereuse_devicehub/migrations/versions/3a3601ac8224_tradedocuments.py index a574047c..5656595e 100644 --- a/ereuse_devicehub/migrations/versions/3a3601ac8224_tradedocuments.py +++ b/ereuse_devicehub/migrations/versions/3a3601ac8224_tradedocuments.py @@ -117,5 +117,6 @@ def upgrade(): def downgrade(): + op.drop_table('action_trade_document', schema=f'{get_inv()}') op.drop_table('trade_document', schema=f'{get_inv()}') diff --git a/ereuse_devicehub/resources/action/models.py b/ereuse_devicehub/resources/action/models.py index 3d12bfda..60c6f91f 100644 --- a/ereuse_devicehub/resources/action/models.py +++ b/ereuse_devicehub/resources/action/models.py @@ -296,9 +296,9 @@ class ActionDevice(db.Model): primary_key=True) -class ActionWithMultipleTradeDocuments(ActionWithMultipleDevices): +class ActionWithMultipleTradeDocuments(Action): documents = relationship(TradeDocument, - backref=backref('actions_multiple_docs', lazy=True, **_sorted_actions), + backref=backref('actions_docs', lazy=True, **_sorted_actions), secondary=lambda: ActionTradeDocument.__table__, order_by=lambda: TradeDocument.id, collection_class=OrderedSet) @@ -1447,8 +1447,36 @@ class Reserve(Organize): class CancelReservation(Organize): """The act of cancelling a reservation.""" +class ConfirmDocument(JoinedTableMixin, ActionWithMultipleTradeDocuments): + """Users confirm the one action trade this confirmation it's link to trade + and the document that confirm + """ + user_id = db.Column(UUID(as_uuid=True), + db.ForeignKey(User.id), + nullable=False, + default=lambda: g.user.id) + user = db.relationship(User, primaryjoin=user_id == User.id) + user_comment = """The user that accept the offer.""" + action_id = db.Column(UUID(as_uuid=True), + db.ForeignKey('action.id'), + nullable=False) + action = db.relationship('Action', + backref=backref('acceptances_document', + uselist=True, + lazy=True, + order_by=lambda: Action.end_time, + collection_class=list), + primaryjoin='Confirm.action_id == Action.id') -class Confirm(JoinedTableMixin, ActionWithMultipleTradeDocuments): + def __repr__(self) -> str: + if self.action.t in ['Trade']: + origin = 'To' + if self.user == self.action.user_from: + origin = 'From' + return '<{0.t} {0.id} accepted by {1}>'.format(self, origin) + + +class Confirm(JoinedTableMixin, ActionWithMultipleDevices): """Users confirm the one action trade this confirmation it's link to trade and the devices that confirm """ @@ -1488,7 +1516,7 @@ class ConfirmRevoke(Confirm): return '<{0.t} {0.id} accepted by {0.user}>'.format(self) -class Trade(JoinedTableMixin, ActionWithMultipleTradeDocuments): +class Trade(JoinedTableMixin, ActionWithMultipleDevices): """Trade actions log the political exchange of devices between users. Every time a trade action is performed, the old user looses its political possession, for example ownership, in favor of another diff --git a/ereuse_devicehub/resources/action/schemas.py b/ereuse_devicehub/resources/action/schemas.py index 8a3d7cf4..7d935750 100644 --- a/ereuse_devicehub/resources/action/schemas.py +++ b/ereuse_devicehub/resources/action/schemas.py @@ -55,6 +55,11 @@ class Action(Thing): class ActionWithOneDevice(Action): + __doc__ = m.ActionWithOneDevice.__doc__ + doc = NestedOn(s_document.TradeDocument, only_query='id') + + +class ActionWithOneDocument(Action): __doc__ = m.ActionWithOneDevice.__doc__ device = NestedOn(s_device.Device, only_query='id') @@ -68,14 +73,6 @@ class ActionWithMultipleDevices(Action): collection_class=OrderedSet) -class ActionWithMultipleTradeDocuments(ActionWithMultipleDevices): - documents = NestedOn(s_document.TradeDocument, - many=True, - required=False, - only_query='id', - collection_class=OrderedSet) - - class Add(ActionWithOneDevice): __doc__ = m.Add.__doc__ @@ -466,7 +463,7 @@ class CancelReservation(Organize): __doc__ = m.CancelReservation.__doc__ -class Confirm(ActionWithMultipleTradeDocuments): +class Confirm(ActionWithMultipleDevices): __doc__ = m.Confirm.__doc__ action = NestedOn('Action', only_query='id') @@ -478,6 +475,13 @@ class Confirm(ActionWithMultipleTradeDocuments): txt = "Device {} not exist in the trade".format(dev.devicehub_id) raise ValidationError(txt) + +class ConfirmDocument(ActionWithOneDocument): + __doc__ = m.Confirm.__doc__ + action = NestedOn('Action', only_query='id') + + @validates_schema + def validate_revoke(self, data: dict): for doc in data.get('documents', []): # if document not exist in the Trade, then this query is wrong if not doc in data['action'].documents: @@ -526,7 +530,7 @@ class Confirm(ActionWithMultipleTradeDocuments): raise ValidationError(txt) -class Revoke(ActionWithMultipleTradeDocuments): +class Revoke(ActionWithMultipleDevices): __doc__ = m.Revoke.__doc__ action = NestedOn('Action', only_query='id') @@ -576,7 +580,51 @@ class Revoke(ActionWithMultipleTradeDocuments): raise ValidationError(txt) -class ConfirmRevoke(ActionWithMultipleTradeDocuments): +class RevokeDocument(ActionWithOneDocument): + __doc__ = m.Revoke.__doc__ + action = NestedOn('Action', only_query='id') + + @validates_schema + def validate_revoke(self, data: dict): + for doc in data.get('documents', []): + # if document not exist in the Trade, then this query is wrong + if not doc in data['action'].documents: + txt = "Document {} not exist in the trade".format(doc.file_name) + raise ValidationError(txt) + + @validates_schema + def validate_documents(self, data): + """Check if there are or no one before confirmation, + This is not checked in the view becouse the list of documents is inmutable + + """ + if not data['devices'] == OrderedSet(): + return + + documents = [] + for doc in data['documents']: + actions = copy.copy(doc.actions) + actions.reverse() + for ac in actions: + if ac == data['action']: + # data['action'] is a Trade action, if this is the first action + # to find mean that this document don't have a confirmation + break + + if ac.t == 'Revoke' and ac.user == g.user: + # this doc is confirmation jet + break + + if ac.t == Confirm.t and ac.user == g.user: + documents.append(doc) + break + + if not documents: + txt = 'No there are documents to revoke' + raise ValidationError(txt) + + +class ConfirmRevoke(ActionWithMultipleDevices): __doc__ = m.ConfirmRevoke.__doc__ action = NestedOn('Action', only_query='id') @@ -636,6 +684,60 @@ class ConfirmRevoke(ActionWithMultipleTradeDocuments): raise ValidationError(txt) +class ConfirmRevokeDocument(ActionWithOneDocument): + __doc__ = m.ConfirmRevoke.__doc__ + action = NestedOn('Action', only_query='id') + + @validates_schema + def validate_revoke(self, data: dict): + for doc in data.get('documents', []): + # if document not exist in the Trade, then this query is wrong + if not doc in data['action'].documents: + txt = "Document {} not exist in the trade".format(doc.file_name) + raise ValidationError(txt) + + @validates_schema + def validate_docs(self, data): + """Check if there are or no one before confirmation, + This is not checked in the view becouse the list of documents is inmutable + + """ + if not data['devices'] == OrderedSet(): + return + + documents = [] + for doc in data['documents']: + actions = copy.copy(doc.actions) + actions.reverse() + for ac in actions: + if ac == data['action']: + # If document have the last action the action for confirm + documents.append(doc) + break + + if ac.t == 'Revoke' and not ac.user == g.user: + # If document is revoke before you can Confirm now + # and revoke is an action of one other user + documents.append(doc) + break + + if ac.t == ConfirmRevoke.t and ac.user == g.user: + # If document is confirmed we don't need confirmed again + break + + if ac.t == Confirm.t: + # if onwer of trade confirm again before than this user Confirm the + # revoke, then is not possible confirm the revoke + # + # If g.user confirm the trade before do a ConfirmRevoke + # then g.user can not to do the ConfirmRevoke more + break + + if not documents: + txt = 'No there are documents with revoke for confirm' + raise ValidationError(txt) + + class Trade(ActionWithMultipleDevices): __doc__ = m.Trade.__doc__ date = DateTime(data_key='date', required=False) diff --git a/ereuse_devicehub/resources/tradedocument/models.py b/ereuse_devicehub/resources/tradedocument/models.py index e8aa4637..5b0939c8 100644 --- a/ereuse_devicehub/resources/tradedocument/models.py +++ b/ereuse_devicehub/resources/tradedocument/models.py @@ -87,7 +87,7 @@ class TradeDocument(Thing): Actions are returned by descending ``created`` time. """ - return sorted(self.actions_multiple_docs, key=lambda x: x.created) + return sorted(self.actions_docs, key=lambda x: x.created) @property def trading(self):