class CategoryBalance(orm.BaseModel): _kind = 71 _use_rule_engine = False from_date = orm.SuperDateTimeProperty('1', required=True, indexed=False) to_date = orm.SuperDateTimeProperty('2', required=True, indexed=False) debit = orm.SuperDecimalProperty('3', required=True, indexed=False) credit = orm.SuperDecimalProperty('4', required=True, indexed=False) balance = orm.SuperDecimalProperty('5', required=True, indexed=False) uom = orm.SuperLocalStructuredProperty(uom.UOM, '6', required=True, indexed=False)
class Line(orm.BaseExpando): '''Notes: We always seqence lines that have debit > 0, and after them come lines that have credit > 0 In case that Entry.balanced=True, sum of all debit amounts must eqal to sum of all credit amounts. To make instances of lines, you must always provide parent key, that is entry's key. Otherwise it will break! Fields can only be properly loaded if are: - loaded from datastore - instanced with proper keyword argument Entry (journal or _model_schema) and Line (parent) ''' _kind = 51 _use_rule_engine = False _journal_fields_loaded = None # Expando # Some queries on Line require "join" with Entry. # That problem can be solved using search engine (or keeping some entry field values copied to lines)! #journal = orm.SuperKeyProperty('1', kind=Journal, required=True) # delete #company = orm.SuperKeyProperty('2', kind='44', required=True) # delete #state = orm.SuperIntegerProperty('3', required=True) # delete #date = orm.SuperDateTimeProperty('4', required=True) # delete sequence = orm.SuperIntegerProperty('5', required=True) categories = orm.SuperKeyProperty('6', kind='47', repeated=True) debit = orm.SuperDecimalProperty('7', required=True, indexed=False) # debit = 0 in case that credit > 0, negative values are forbidden. credit = orm.SuperDecimalProperty('8', required=True, indexed=False) # credit = 0 in case that debit > 0, negative values are forbidden. uom = orm.SuperLocalStructuredProperty(uom.UOM, '9', required=True) def __init__(self, *args, **kwargs): '''Caution! Making instances of Line() inside a transaction may cause performing non-entity group queries (see in add journal fields). As for get()s itself it will use in-memory cache when it can. ''' entry_key = kwargs.get('parent') complete_key = kwargs.get('key') # Also observe the complete key instances. journal_key = kwargs.pop('journal', None) # if journal key is provided use it. This is generally retarded but only way so far. if journal_key is not None: self.add_journal_fields(journal_key) elif entry_key is not None: self.add_journal_fields(entry_key.entity.journal) elif complete_key is not None: self.add_journal_fields(complete_key.parent_entity.journal) # we intentionally call this code before __init__ due __init__ ability to deepcopy the entity and other things beside that. super(Line, self).__init__(*args, **kwargs) def add_journal_fields(self, journal_key=None): if not self._journal_fields_loaded: if not journal_key and self.key: journal_key = self.parent_entity.journal journal = journal_key.get() if journal is None: raise Exception('Cannot find journal with key %r.' % journal_key) self._clone_properties() for name, prop in journal.line_fields.iteritems(): # still not 100% sure if we need to deepcopy these properties that get loaded from datastore prop._code_name = name self._properties[prop._name] = prop self.add_output(name) self._journal_fields_loaded = True def get_kind(self): return '%s_%s' % (self._get_kind(), self.journal.id()) def get_fields(self): fields = super(Line, self.__class__).get_fields() # Calling parent get_fields. for name, prop in self._properties.iteritems(): fields[prop._code_name] = prop return fields def _get_property_for(self, p, indexed=True, depth=0): '''It is always easier to override _get_property_for because you immidiately get self which tells you on which entity you operate, and if the entity itself has a key. ''' if self.key is None or self.key.parent() is None: raise Exception('Cannot load properties of %s because it does not have parent key provided.' % self) else: if self._journal_fields_loaded is None: self.add_journal_fields() return super(Line, self)._get_property_for(p, indexed, depth) @classmethod def get_meta(cls): '''This function returns dictionary of meta data (not stored or dynamically generated data) of the model. The returned dictionary can be transalted into other understandable code to clients (e.g. JSON). ''' dic = {} dic['_actions'] = getattr(cls, '_actions', []) dic.update(super(Line, cls).get_fields()) return dic
class Journal(orm.BaseExpando): _kind = 49 created = orm.SuperDateTimeProperty('1', required=True, auto_now_add=True) updated = orm.SuperDateTimeProperty('2', required=True, auto_now=True) name = orm.SuperStringProperty('3', required=True) state = orm.SuperStringProperty('4', required=True, default='draft', choices=['draft', 'active', 'decommissioned']) entry_fields = orm.SuperPickleProperty('5', required=True, indexed=False, compressed=False) line_fields = orm.SuperPickleProperty('6', required=True, indexed=False, compressed=False) _default_indexed = False _virtual_fields = { '_records': orm.SuperRecordProperty('49'), '_code': orm.SuperComputedProperty(lambda self: self.key_id_str), '_transaction_actions': orm.SuperRemoteStructuredProperty(Action, repeated=True), '_transaction_plugin_groups': orm.SuperRemoteStructuredProperty(PluginGroup, repeated=True) } _global_role = GlobalRole( permissions=[ orm.ActionPermission('49', [orm.Action.build_key('49', 'prepare'), orm.Action.build_key('49', 'create'), orm.Action.build_key('49', 'read'), orm.Action.build_key('49', 'update'), orm.Action.build_key('49', 'delete'), orm.Action.build_key('49', 'search'), orm.Action.build_key('49', 'activate'), orm.Action.build_key('49', 'decommission')], False, 'entity._original.namespace_entity._original.state != "active"'), orm.ActionPermission('49', [orm.Action.build_key('49', 'delete')], False, 'entity._original.state != "draft"'), orm.ActionPermission('49', [orm.Action.build_key('49', 'activate')], False, 'entity._original.state == "active"'), orm.ActionPermission('49', [orm.Action.build_key('49', 'decommission')], False, 'entity._is_system or entity._original.state != "active"'), orm.FieldPermission('49', ['created', 'updated', 'state'], False, None, 'True'), orm.FieldPermission('49', ['created', 'updated', 'name', 'state', 'entry_fields', 'line_fields', '_records', '_code', '_transaction_actions', '_transaction_plugin_groups'], False, False, 'entity._original.namespace_entity._original.state != "active"'), orm.FieldPermission('49', ['created', 'updated', 'name', 'state', 'entry_fields', 'line_fields', '_records', '_code'], False, None, 'entity._original.state != "draft"'), orm.FieldPermission('49', ['_transaction_actions', '_transaction_plugin_groups.name', '_transaction_plugin_groups.subscriptions', '_transaction_plugin_groups.active', '_transaction_plugin_groups.sequence', '_transaction_plugin_groups.transactional'], False, None, 'entity._is_system'), orm.FieldPermission('49', ['_transaction_plugin_groups.plugins'], False, None, 'entity._is_system and entity._original._transaction_plugin_groups.name != "User Plugins"'), # @todo Missing index between _transaction_plugin_groups and name! orm.FieldPermission('49', ['state'], True, None, '(action.key_id_str == "activate" and entity.state == "active") or (action.key_id_str == "decommission" and entity.state == "decommissioned")') ] ) _actions = [ orm.Action( key=orm.Action.build_key('49', 'prepare'), arguments={ 'domain': orm.SuperKeyProperty(kind='6', required=True) }, _plugin_groups=[ orm.PluginGroup( plugins=[ Context(), Read(), RulePrepare(), RuleExec(), Set(cfg={'d': {'output.entity': '_journal'}}) ] ) ] ), orm.Action( key=orm.Action.build_key('49', 'create'), arguments={ 'domain': orm.SuperKeyProperty(kind='6', required=True), '_code': orm.SuperStringProperty(required=True, max_size=64), # Regarding max_size, take a look at the transaction.JournalUpdateRead() plugin! 'name': orm.SuperStringProperty(required=True), 'entry_fields': orm.SuperPropertyStorageProperty(required=True, cfg=JOURNAL_FIELDS), 'line_fields': orm.SuperPropertyStorageProperty(required=True, cfg=JOURNAL_FIELDS) }, _plugin_groups=[ orm.PluginGroup( plugins=[ Context(), Read(), Set(cfg={'s': {'_journal.state': 'draft'}, 'd': {'_journal.name': 'input.name', '_journal.entry_fields': 'input.entry_fields', '_journal.line_fields': 'input.line_fields'}}), RulePrepare(), RuleExec() ] ), orm.PluginGroup( transactional=True, plugins=[ Write(), Set(cfg={'d': {'output.entity': '_journal'}}), CallbackNotify(), CallbackExec() ] ) ] ), orm.Action( key=orm.Action.build_key('49', 'read'), arguments={ 'key': orm.SuperKeyProperty(kind='49', required=True), 'read_arguments': orm.SuperJsonProperty() }, _plugin_groups=[ orm.PluginGroup( plugins=[ Context(), Read(), RulePrepare(), RuleExec(), Set(cfg={'d': {'output.entity': '_journal'}}) ] ) ] ), orm.Action( key=orm.Action.build_key('49', 'update'), arguments={ 'key': orm.SuperKeyProperty(kind='49', required=True), 'name': orm.SuperStringProperty(required=True), 'entry_fields': orm.SuperPropertyStorageProperty(required=True, cfg=JOURNAL_FIELDS), 'line_fields': orm.SuperPropertyStorageProperty(required=True, cfg=JOURNAL_FIELDS), '_transaction_actions': orm.SuperLocalStructuredProperty(Action, repeated=True), '_transaction_plugin_groups': orm.SuperLocalStructuredProperty(PluginGroup, repeated=True) }, _plugin_groups=[ orm.PluginGroup( plugins=[ Context(), Read(), Set(cfg={'d': {'_journal.name': 'input.name', '_journal.entry_fields': 'input.entry_fields', '_journal.line_fields': 'input.line_fields', '_journal._transaction_actions': 'input._transaction_actions', '_journal._transaction_plugin_groups': 'input._transaction_plugin_groups'}}), RulePrepare(), RuleExec() ] ), orm.PluginGroup( transactional=True, plugins=[ Write(), Set(cfg={'d': {'output.entity': '_journal'}}), CallbackNotify(), CallbackExec() ] ) ] ), orm.Action( key=orm.Action.build_key('49', 'delete'), arguments={ 'key': orm.SuperKeyProperty(kind='49', required=True) }, _plugin_groups=[ orm.PluginGroup( plugins=[ Context(), Read(), RulePrepare(), RuleExec() ] ), orm.PluginGroup( transactional=True, plugins=[ Delete(), Set(cfg={'d': {'output.entity': '_journal'}}), CallbackNotify(), CallbackExec() ] ) ] ), orm.Action( key=orm.Action.build_key('49', 'search'), arguments={ 'domain': orm.SuperKeyProperty(kind='6', required=True), 'search': orm.SuperSearchProperty( default={'filters': [], 'orders': [{'field': 'name', 'operator': 'asc'}]}, cfg={ 'search_arguments': {'kind': '49', 'options': {'limit': settings.SEARCH_PAGE}}, 'filters': {'name': orm.SuperStringProperty(), 'state': orm.SuperStringProperty(choices=[])}, 'indexes': [{'orders': [('name', ['asc', 'desc'])]}, {'orders': [('state', ['asc', 'desc'])]}, {'filters': [('name', ['==', '!='])], 'orders': [('name', ['asc', 'desc'])]}, {'filters': [('state', ['==', '!='])], 'orders': [('name', ['asc', 'desc'])]}, {'filters': [('state', ['==', '!=']), ('name', ['==', '!='])], 'orders': [('name', ['asc', 'desc'])]}] } ) }, _plugin_groups=[ orm.PluginGroup( plugins=[ Context(), Read(), RulePrepare(), RuleExec(), Search(), RulePrepare(cfg={'path': '_entities'}), Set(cfg={'d': {'output.entities': '_entities', 'output.cursor': '_cursor', 'output.more': '_more'}}) ] ) ] ), orm.Action( key=orm.Action.build_key('49', 'activate'), arguments={ 'key': orm.SuperKeyProperty(kind='49', required=True), 'message': orm.SuperTextProperty(required=True) }, _plugin_groups=[ orm.PluginGroup( plugins=[ Context(), Read(), Set(cfg={'s': {'_journal.state': 'active'}}), RulePrepare(), RuleExec() ] ), orm.PluginGroup( transactional=True, plugins=[ Write(cfg={'dra': {'message': 'input.message'}}), CallbackNotify(), CallbackExec() ] ), orm.PluginGroup( plugins=[ RulePrepare(), Set(cfg={'d': {'output.entity': '_journal'}}) ] ) ] ), orm.Action( key=orm.Action.build_key('49', 'decommission'), arguments={ 'key': orm.SuperKeyProperty(kind='49', required=True), 'message': orm.SuperTextProperty(required=True) }, _plugin_groups=[ orm.PluginGroup( plugins=[ Context(), Read(), Set(cfg={'s': {'_journal.state': 'decommissioned'}}), RulePrepare(), RuleExec() ] ), orm.PluginGroup( transactional=True, plugins=[ Write(cfg={'dra': {'message': 'input.message'}}), CallbackNotify(), CallbackExec() ] ), orm.PluginGroup( plugins=[ RulePrepare(), Set(cfg={'d': {'output.entity': '_journal'}}) ] ) ] ) ] @classmethod def prepare_key(cls, input, **kwargs): code = input.get('_code') return cls.build_key(code, namespace=kwargs.get('namespace')) # @todo Possible prefix? @property def _is_system(self): return self.key_id_str.startswith('system_')
class Category(orm.BaseExpando): _kind = 47 created = orm.SuperDateTimeProperty('1', required=True, auto_now_add=True) updated = orm.SuperDateTimeProperty('2', required=True, auto_now=True) parent_record = orm.SuperKeyProperty('3', kind='47') name = orm.SuperStringProperty('4', required=True) complete_name = orm.SuperTextProperty('5', required=True) active = orm.SuperBooleanProperty('6', required=True, default=True) _default_indexed = False ###################################################### # Primitive example of real time balance calculator! # ###################################################### @classmethod def _get_children(cls, parent_records, children): entities = [] for parent_record in parent_records: entities.extend(cls.query(cls.parent_record == parent_record).fetch(keys_only=True)) if len(entities): children.extend(entities) cls._get_children(entities, children) @classmethod def _get_balance(cls, category): debit = 0 credit = 0 lines = Line.query(Line.categories.IN(category._children_records)).fetch() for line in lines: debit += line.debit credit += line.credit return (debit, credit, (debit - credit)) @classmethod def _post_get_hook(cls, key, future): # @todo Missing super extension! entity = future.get_result() if entity is not None and entity.key: entity._children_records = [entity.key] entity._get_children([entity.key], entity._children_records) entity._debit, entity._credit, entity._balance = entity._get_balance(entity) ################### # End of example! # ################### _expando_fields = { 'description': orm.SuperTextProperty('7'), 'balances': orm.SuperLocalStructuredProperty(CategoryBalance, '8', repeated=True) } _virtual_fields = { '_records': orm.SuperRecordProperty('47'), '_code': orm.SuperComputedProperty(lambda self: self.key_id_str) #'_debit': orm.SuperComputedProperty(lambda self: self._debit), #'_credit': orm.SuperComputedProperty(lambda self: self._credit), #'_balance': orm.SuperComputedProperty(lambda self: self._balance), } _global_role = GlobalRole( permissions=[ orm.ActionPermission('47', [orm.Action.build_key('47', 'prepare'), orm.Action.build_key('47', 'create'), orm.Action.build_key('47', 'read'), orm.Action.build_key('47', 'update'), orm.Action.build_key('47', 'delete'), orm.Action.build_key('47', 'search')], False, 'entity._original.namespace_entity._original.state != "active"'), orm.ActionPermission('47', [orm.Action.build_key('47', 'create'), orm.Action.build_key('47', 'update'), orm.Action.build_key('47', 'delete')], False, 'entity._is_system'), orm.ActionPermission('47', [orm.Action.build_key('47', 'delete')], False, 'entity._is_used'), orm.FieldPermission('47', ['created', 'updated'], False, None, 'True'), orm.FieldPermission('47', ['created', 'updated', 'parent_record', 'name', 'complete_name', 'active', 'description', 'balances', '_records', '_code'], False, False, 'entity._original.namespace_entity._original.state != "active"'), orm.FieldPermission('47', ['created', 'updated', 'parent_record', 'name', 'complete_name', 'active', 'description', 'balances', '_records', '_code'], False, None, 'entity._is_system') ] ) _actions = [ orm.Action( key=orm.Action.build_key('47', 'prepare'), arguments={ 'domain': orm.SuperKeyProperty(kind='6', required=True) }, _plugin_groups=[ orm.PluginGroup( plugins=[ Context(), Read(), RulePrepare(), RuleExec(), Set(cfg={'d': {'output.entity': '_category'}}) ] ) ] ), orm.Action( key=orm.Action.build_key('47', 'create'), arguments={ 'domain': orm.SuperKeyProperty(kind='6', required=True), '_code': orm.SuperStringProperty(required=True, max_size=64), # Regarding max_size, take a look at the transaction.CategoryUpdateRead() plugin! 'parent_record': orm.SuperKeyProperty(kind='47'), 'name': orm.SuperStringProperty(required=True), 'active': orm.SuperBooleanProperty(required=True, default=True), 'description': orm.SuperTextProperty() }, _plugin_groups=[ orm.PluginGroup( plugins=[ Context(), Read(), CategoryUpdateSet(), # @todo Unless we decide to implement that complete_name handling property, this will stay. RulePrepare(), RuleExec() ] ), orm.PluginGroup( transactional=True, plugins=[ Write(), Set(cfg={'d': {'output.entity': '_category'}}), CallbackNotify(), CallbackExec() ] ) ] ), orm.Action( key=orm.Action.build_key('47', 'read'), arguments={ 'key': orm.SuperKeyProperty(kind='47', required=True), 'read_arguments': orm.SuperJsonProperty() }, _plugin_groups=[ orm.PluginGroup( plugins=[ Context(), Read(), RulePrepare(), RuleExec(), Set(cfg={'d': {'output.entity': '_category'}}) ] ) ] ), orm.Action( key=orm.Action.build_key('47', 'update'), arguments={ 'key': orm.SuperKeyProperty(kind='47', required=True), 'parent_record': orm.SuperKeyProperty(kind='47'), 'name': orm.SuperStringProperty(required=True), 'active': orm.SuperBooleanProperty(required=True, default=True), 'description': orm.SuperTextProperty() }, _plugin_groups=[ orm.PluginGroup( plugins=[ Context(), Read(), CategoryUpdateSet(), # @todo Unless we decide to implement that complete_name handling property, this will stay. RulePrepare(), RuleExec() ] ), orm.PluginGroup( transactional=True, plugins=[ Write(), Set(cfg={'d': {'output.entity': '_category'}}), CallbackNotify(), CallbackExec() ] ) ] ), orm.Action( key=orm.Action.build_key('47', 'delete'), arguments={ 'key': orm.SuperKeyProperty(kind='47', required=True) }, _plugin_groups=[ orm.PluginGroup( plugins=[ Context(), Read(), RulePrepare(), RuleExec() ] ), orm.PluginGroup( transactional=True, plugins=[ Delete(), Set(cfg={'d': {'output.entity': '_category'}}), CallbackNotify(), CallbackExec() ] ) ] ), orm.Action( key=orm.Action.build_key('47', 'search'), arguments={ 'domain': orm.SuperKeyProperty(kind='6', required=True), 'search': orm.SuperSearchProperty( default={'filters': [{'field': 'active', 'value': True, 'operator': '=='}], 'orders': [{'field': 'name', 'operator': 'asc'}]}, cfg={ 'search_by_keys': True, 'search_arguments': {'kind': '47', 'options': {'limit': settings.SEARCH_PAGE}}, 'filters': {'name': orm.SuperStringProperty(), 'active': orm.SuperBooleanProperty()}, 'indexes': [{'orders': [('name', ['asc', 'desc'])]}, {'orders': [('created', ['asc', 'desc'])]}, {'orders': [('updated', ['asc', 'desc'])]}, {'orders': [('active', ['asc', 'desc'])]}, {'filters': [('name', ['==', '!='])], 'orders': [('name', ['asc', 'desc'])]}, {'filters': [('active', ['=='])], 'orders': [('name', ['asc', 'desc'])]}, {'filters': [('active', ['==']), ('name', ['==', '!='])], 'orders': [('name', ['asc', 'desc'])]}] } ) }, _plugin_groups=[ orm.PluginGroup( plugins=[ Context(), Read(), RulePrepare(), RuleExec(), Search(), RulePrepare(cfg={'path': '_entities'}), Set(cfg={'d': {'output.entities': '_entities', 'output.cursor': '_cursor', 'output.more': '_more'}}) ] ) ] ) ] @classmethod def prepare_key(cls, input, **kwargs): code = input.get('_code') return cls.build_key(code, namespace=kwargs.get('namespace')) # @todo Possible prefix? @property def _is_system(self): return self.key_id_str.startswith('system_') @property def _is_used(self): if self.key.id() is None: return False category = self.query(self.__class__.parent_record == self.key).get() line = Line.query(Line.categories == self.key).get() return (category is not None) or (line is not None)
class Widget(orm.BaseExpando): _kind = 62 _use_search_engine = True # @todo This is extra added for testing functions of google seach! name = orm.SuperStringProperty( '1', required=True, searchable=True ) # @todo This is extra added for testing functions of google seach! sequence = orm.SuperIntegerProperty( '2', required=True, searchable=True ) # @todo This is extra added for testing functions of google seach! active = orm.SuperBooleanProperty( '3', required=True, default=True, searchable=True ) # @todo This is extra added for testing functions of google seach! role = orm.SuperKeyProperty('4', kind='60', required=True) search_form = orm.SuperBooleanProperty('5', required=True, indexed=False, default=True) filters = orm.SuperLocalStructuredProperty(Filter, '6', repeated=True) _default_indexed = False _virtual_fields = {'_records': orm.SuperRecordProperty('62')} _global_role = GlobalRole(permissions=[ orm.ActionPermission( '62', [ orm.Action.build_key('62', 'prepare'), orm.Action.build_key('62', 'create'), orm.Action.build_key('62', 'read'), orm.Action.build_key('62', 'update'), orm.Action.build_key('62', 'delete'), orm.Action.build_key('62', 'search'), orm.Action.build_key('62', 'build_menu') ], False, 'entity._original.namespace_entity._original.state != "active"'), orm.ActionPermission('62', [ orm.Action.build_key('62', 'create'), orm.Action.build_key('62', 'update'), orm.Action.build_key('62', 'delete') ], False, 'entity._is_system'), orm.FieldPermission( '62', [ 'name', 'sequence', 'active', 'role', 'search_form', 'filters', '_records' ], False, False, 'entity._original.namespace_entity._original.state != "active"'), orm.FieldPermission('62', [ 'name', 'sequence', 'active', 'role', 'search_form', 'filters', '_records' ], False, None, 'entity._is_system'), orm.FieldPermission( '62', ['role'], False, None, '(action.key_id_str == "create" or action.key_id_str == "update") and (entity.role and entity.role._namespace != entity._original.key_namespace)' ) ]) _actions = [ orm.Action(key=orm.Action.build_key('62', 'prepare'), arguments={ 'domain': orm.SuperKeyProperty(kind='6', required=True) }, _plugin_groups=[ orm.PluginGroup(plugins=[ Context(), Read(), RulePrepare(), RuleExec(), Set(cfg={'d': { 'output.entity': '_widget' }}) ]) ]), orm.Action( key=orm.Action.build_key('62', 'create'), arguments={ 'domain': orm.SuperKeyProperty(kind='6', required=True), 'name': orm.SuperStringProperty(required=True), 'sequence': orm.SuperIntegerProperty(required=True), 'active': orm.SuperBooleanProperty(default=True), 'role': orm.SuperKeyProperty(kind='60', required=True), 'search_form': orm.SuperBooleanProperty(default=True), 'filters': orm.SuperLocalStructuredProperty(Filter, repeated=True) }, _plugin_groups=[ orm.PluginGroup(plugins=[ Context(), Read(), Set( cfg={ 'd': { '_widget.name': 'input.name', '_widget.sequence': 'input.sequence', '_widget.active': 'input.active', '_widget.role': 'input.role', '_widget.search_form': 'input.search_form', '_widget.filters': 'input.filters' } }), RulePrepare(), RuleExec() ]), orm.PluginGroup( transactional=True, plugins=[ Write(), Set(cfg={'d': { 'output.entity': '_widget' }}), CallbackNotify(), CallbackExec() ]) ]), orm.Action(key=orm.Action.build_key('62', 'read'), arguments={ 'key': orm.SuperKeyProperty(kind='62', required=True), 'read_arguments': orm.SuperJsonProperty() }, _plugin_groups=[ orm.PluginGroup(plugins=[ Context(), Read(), RulePrepare(), RuleExec(), Set(cfg={'d': { 'output.entity': '_widget' }}) ]) ]), orm.Action( key=orm.Action.build_key('62', 'update'), arguments={ 'key': orm.SuperKeyProperty(kind='62', required=True), 'name': orm.SuperStringProperty(required=True), 'sequence': orm.SuperIntegerProperty(required=True), 'active': orm.SuperBooleanProperty(default=True), 'role': orm.SuperKeyProperty(kind='60', required=True), 'search_form': orm.SuperBooleanProperty(default=True), 'filters': orm.SuperLocalStructuredProperty(Filter, repeated=True), 'read_arguments': orm.SuperJsonProperty() }, _plugin_groups=[ orm.PluginGroup(plugins=[ Context(), Read(), Set( cfg={ 'd': { '_widget.name': 'input.name', '_widget.sequence': 'input.sequence', '_widget.active': 'input.active', '_widget.role': 'input.role', '_widget.search_form': 'input.search_form', '_widget.filters': 'input.filters' } }), RulePrepare(), RuleExec() ]), orm.PluginGroup( transactional=True, plugins=[ Write(), Set(cfg={'d': { 'output.entity': '_widget' }}), CallbackNotify(), CallbackExec() ]) ]), orm.Action( key=orm.Action.build_key('62', 'delete'), arguments={'key': orm.SuperKeyProperty(kind='62', required=True)}, _plugin_groups=[ orm.PluginGroup( plugins=[Context( ), Read(), RulePrepare(), RuleExec()]), orm.PluginGroup( transactional=True, plugins=[ Delete(), Set(cfg={'d': { 'output.entity': '_widget' }}), CallbackNotify(), CallbackExec() ]) ]), orm.Action( key=orm.Action.build_key('62', 'search'), arguments={ 'domain': orm.SuperKeyProperty(kind='6', required=True), 'search': orm.SuperSearchProperty( default={ 'filters': [], 'orders': [{ 'field': 'sequence', 'operator': 'asc' }] }, cfg={ 'use_search_engine': True, # @todo This is extra added for testing functions of google seach! 'search_arguments': { 'kind': '62', 'options': { 'limit': settings.SEARCH_PAGE } }, 'filters': { 'name': orm.SuperStringProperty(), 'role': orm.SuperKeyProperty(kind='60'), 'active': orm.SuperBooleanProperty() }, 'orders': { 'name': { 'default_value': { 'asc': 'a', 'desc': 'z' } }, # @todo This is extra added for testing functions of google seach! 'sequence': { 'default_value': { 'asc': 0, 'desc': 1000000 } } }, # @todo This is extra added for testing functions of google seach! 'indexes': [{ 'orders': [('name', ['asc', 'desc'])] }, { 'orders': [('sequence', ['asc', 'desc'])] }, { 'filters': [('name', ['==', '!='])], 'orders': [('sequence', ['asc', 'desc'])] }, { 'filters': [('active', ['=='])], 'orders': [('sequence', ['asc', 'desc'])] }, { 'filters': [('role', ['==', '!='])], 'orders': [('sequence', ['asc', 'desc'])] }, { 'filters': [('active', ['==']), ('name', ['==', '!='])], 'orders': [('sequence', ['asc', 'desc'])] }, { 'filters': [('active', ['==']), ('role', ['==', '!='])], 'orders': [('sequence', ['asc', 'desc'])] }] }) }, _plugin_groups=[ orm.PluginGroup(plugins=[ Context(), Read(), RulePrepare(), RuleExec(), Search(), RulePrepare(cfg={'path': '_entities'}), Set( cfg={ 'd': { 'output.entities': '_entities', 'output.cursor': '_cursor', 'output.more': '_more' } }) ]) ]), orm.Action(key=orm.Action.build_key('62', 'build_menu'), arguments={ 'domain': orm.SuperKeyProperty(kind='6', required=True) }, _plugin_groups=[ orm.PluginGroup(plugins=[ Context(), Read(), RulePrepare(), RuleExec(), WidgetBuildMenu(), Set( cfg={ 'd': { 'output.menu': '_widgets', 'output.domain': 'domain' } }) ]) ]) ] @property def _is_system(self): return self.key_id_str.startswith('system_')
class User(orm.BaseExpando): _kind = 0 _use_memcache = True created = orm.SuperDateTimeProperty('1', required=True, auto_now_add=True) updated = orm.SuperDateTimeProperty('2', required=True, auto_now=True) identities = orm.SuperStructuredProperty(Identity, '3', repeated=True) # Soft limit 100 instances. emails = orm.SuperStringProperty('4', repeated=True) # Soft limit 100 instances. state = orm.SuperStringProperty('5', required=True, choices=['active', 'suspended']) # @todo Shall we disable indexing here? sessions = orm.SuperLocalStructuredProperty(Session, '6', repeated=True) # Soft limit 100 instances. domains = orm.SuperKeyProperty('7', kind='6', repeated=True) # Soft limit 100 instances. @todo Shall we disable indexing here? _default_indexed = False _virtual_fields = { 'ip_address': orm.SuperComputedProperty(lambda self: os.environ.get('REMOTE_ADDR')), '_primary_email': orm.SuperComputedProperty(lambda self: self.primary_email()), '_records': orm.SuperRecordProperty('0') } _global_role = GlobalRole( permissions=[ orm.ActionPermission('0', orm.Action.build_key('0', 'login'), True, 'entity._is_guest or entity._original.state == "active"'), orm.ActionPermission('0', [orm.Action.build_key('0', 'read'), orm.Action.build_key('0', 'update'), orm.Action.build_key('0', 'logout'), orm.Action.build_key('0', 'read_domains')], True, 'not entity._is_guest and user.key == entity._original.key'), orm.FieldPermission('0', ['created', 'updated', 'state', 'domains'], False, True, 'not user._is_guest and user.key == entity._original.key'), orm.FieldPermission('0', ['identities', 'emails', 'sessions', '_primary_email'], True, True, 'not user._is_guest and user.key == entity._original.key'), # User is unit of administration, hence root admins need control over it! # Root admins can always: read user; search for users (exclusively); # read users history (exclusively); perform sudo operations (exclusively). orm.ActionPermission('0', [orm.Action.build_key('0', 'read'), orm.Action.build_key('0', 'search'), orm.Action.build_key('0', 'sudo')], True, 'user._root_admin'), orm.FieldPermission('0', ['created', 'updated', 'identities', 'emails', 'state', 'sessions', 'domains', 'ip_address', '_primary_email', '_records'], None, True, 'user._root_admin'), orm.FieldPermission('0', ['state'], True, None, 'action.key_id_str == "sudo" and user._root_admin') ] ) _actions = [ orm.Action( key=orm.Action.build_key('0', 'login'), arguments={ 'login_method': orm.SuperStringProperty(required=True, choices=settings.LOGIN_METHODS.keys()), 'code': orm.SuperStringProperty(), 'error': orm.SuperStringProperty() }, _plugin_groups=[ orm.PluginGroup( plugins=[ Context(), UserLoginInit(cfg={'methods': settings.LOGIN_METHODS}) ] ), orm.PluginGroup( transactional=True, plugins=[ UserLoginWrite() ] ) ] ), orm.Action( key=orm.Action.build_key('0', 'read'), arguments={ 'key': orm.SuperKeyProperty(kind='0', required=True), 'read_arguments': orm.SuperJsonProperty() }, _plugin_groups=[ orm.PluginGroup( plugins=[ Context(), Read(), RulePrepare(cfg={'skip_user_roles': True}), RuleExec(), Set(cfg={'d': {'output.entity': '_user'}}) ] ) ] ), orm.Action( key=orm.Action.build_key('0', 'update'), arguments={ 'key': orm.SuperKeyProperty(kind='0', required=True), 'primary_email': orm.SuperStringProperty(), 'disassociate': orm.SuperStringProperty(repeated=True), 'read_arguments': orm.SuperJsonProperty() }, _plugin_groups=[ orm.PluginGroup( plugins=[ Context(), Read(), UserUpdateSet(), RulePrepare(cfg={'skip_user_roles': True}), RuleExec() ] ), orm.PluginGroup( transactional=True, plugins=[ Write(), Set(cfg={'d': {'output.entity': '_user'}}), # CallbackNotify(), # CallbackExec() notify cannot work here ] ) ] ), orm.Action( key=orm.Action.build_key('0', 'search'), arguments={ 'search': orm.SuperSearchProperty( default={'filters': [], 'orders': [{'field': 'created', 'operator': 'desc'}]}, cfg={ 'search_arguments': {'kind': '0', 'options': {'limit': settings.SEARCH_PAGE}}, 'filters': {'emails': orm.SuperStringProperty(), 'state': orm.SuperStringProperty()}, 'indexes': [{'orders': [('emails', ['asc', 'desc'])]}, {'orders': [('created', ['asc', 'desc'])]}, {'orders': [('updated', ['asc', 'desc'])]}, {'filters': [('emails', ['==', '!='])], 'orders': [('created', ['asc', 'desc'])]}, {'filters': [('state', ['==', '!='])], 'orders': [('created', ['asc', 'desc'])]}] } ) }, _plugin_groups=[ orm.PluginGroup( plugins=[ Context(), Read(), RulePrepare(cfg={'skip_user_roles': True}), RuleExec(), Search(), RulePrepare(cfg={'path': '_entities', 'skip_user_roles': True}), Set(cfg={'d': {'output.entities': '_entities', 'output.cursor': '_cursor', 'output.more': '_more'}}) ] ) ] ), # @todo Treba obratiti paznju na to da suspenzija usera ujedno znaci # i izuzimanje svih negativnih i neutralnih feedbackova koje je user ostavio dok je bio aktivan. orm.Action( key=orm.Action.build_key('0', 'sudo'), arguments={ 'key': orm.SuperKeyProperty(kind='0', required=True), 'state': orm.SuperStringProperty(required=True, choices=['active', 'suspended']), 'message': orm.SuperStringProperty(required=True), 'note': orm.SuperStringProperty() }, _plugin_groups=[ orm.PluginGroup( plugins=[ Context(), Read(), Set(cfg={'d': {'_user.state': 'input.state'}, 's': {'_user.sessions': []}}), RulePrepare(cfg={'skip_user_roles': True}), RuleExec(), ] ), orm.PluginGroup( transactional=True, plugins=[ Write(cfg={'dra': {'message': 'input.message', 'note': 'input.note'}}), Set(cfg={'d': {'output.entity': '_user'}}), # CallbackNotify(), # CallbackExec() notify cannot work here ] ) ] ), orm.Action( key=orm.Action.build_key('0', 'logout'), arguments={ 'key': orm.SuperKeyProperty(kind='0', required=True) }, _plugin_groups=[ orm.PluginGroup( plugins=[ Context(), Read(), Set(cfg={'s': {'_user.sessions': []}}), RulePrepare(cfg={'skip_user_roles': True}), RuleExec() ] ), orm.PluginGroup( transactional=True, plugins=[ Write(cfg={'dra': {'ip_address': '_user.ip_address'}}), UserLogoutOutput() ] ) ] ), # We need this action in order to properly prepare entities for client side access control! orm.Action( key=orm.Action.build_key('0', 'read_domains'), arguments={ 'key': orm.SuperKeyProperty(kind='0', required=True) }, _plugin_groups=[ orm.PluginGroup( plugins=[ Context(), UserReadDomains() ] ) ] ) ] def get_output(self): dic = super(User, self).get_output() dic.update({'_csrf': self._csrf, # We will need the csrf but it has to be incorporated into security mechanism (http://en.wikipedia.org/wiki/Cross-site_request_forgery). '_is_guest': self._is_guest, '_root_admin': self._root_admin}) return dic @property def _root_admin(self): return self._primary_email in settings.ROOT_ADMINS @property def _is_taskqueue(self): return mem.temp_get('_current_request_is_taskqueue') @property def _is_cron(self): return mem.temp_get('_current_request_is_cron') def set_taskqueue(self, is_it): return mem.temp_set('_current_request_is_taskqueue', is_it) def set_cron(self, is_it): return mem.temp_set('_current_request_is_cron', is_it) def primary_email(self): if not self.identities.value: return None for identity in self.identities.value: if identity.primary == True: return identity.email return identity.email @property def _csrf(self): session = self.current_user_session() if not session: return None return hashlib.md5(session.session_id).hexdigest() @property def _is_guest(self): return self.key is None @classmethod def set_current_user(cls, user, session=None): mem.temp_set('_current_user', user) mem.temp_set('_current_user_session', session) @classmethod def current_user(cls): current_user = mem.temp_get('_current_user') if not current_user: current_user = cls() cls.set_current_user(current_user) return current_user @classmethod def get_system_user(cls): user_key = cls.build_key('system') user = user_key.get() if not user: identities = [Identity(email='System', identity='1-0', associated=True, primary=True)] user = cls(key=user_key, state='active', emails=['System'], identities=identities) user.put() return user @classmethod def current_user_session(cls): return mem.temp_get('_current_user_session') def session_by_id(self, session_id): for session in self.sessions.value: if session.session_id == session_id: return session return None @classmethod def set_current_user_from_auth_code(cls, auth_code): try: user_key, session_id = auth_code.split('|') except: return False # Fail silently if the authorization code is not set properly, or it is corrupted somehow. if not session_id: return False # Fail silently if the session id is not found in the split sequence. user_key = orm.Key(urlsafe=user_key) if user_key.kind() != cls.get_kind(): return False # Fail silently if the kind is not valid user = user_key.get() if user and user.key_id != 'system': user.read() session = user.session_by_id(session_id) if session: cls.set_current_user(user, session) return user
class Addresses(orm.BaseModel): _kind = 77 addresses = orm.SuperLocalStructuredProperty(Address, '1', repeated=True) _virtual_fields = {'_records': orm.SuperRecordProperty('77')} _global_role = GlobalRole(permissions=[ orm.ActionPermission( '77', [ orm.Action.build_key('77', 'update'), orm.Action.build_key('77', 'read') ], True, 'entity._original.key_parent == user.key and not user._is_guest'), orm.FieldPermission( '77', ['addresses', '_records'], True, True, 'entity._original.key_parent == user.key and not user._is_guest') ]) _actions = [ orm.Action( key=orm.Action.build_key('77', 'update'), arguments={ 'user': orm.SuperKeyProperty(kind='0', required=True), 'addresses': orm.SuperLocalStructuredProperty(Address, repeated=True), 'read_arguments': orm.SuperJsonProperty() }, _plugin_groups=[ orm.PluginGroup(plugins=[ Context(), Read(), Set(cfg={'d': { '_addresses.addresses': 'input.addresses' }}), AddressesUpdateSet(), RulePrepare(cfg={'skip_user_roles': True}), RuleExec() ]), orm.PluginGroup( transactional=True, plugins=[ Write(), Set(cfg={'d': { 'output.entity': '_addresses' }}) ]) ]), orm.Action(key=orm.Action.build_key('77', 'read'), arguments={ 'user': orm.SuperKeyProperty(kind='0', required=True), 'read_arguments': orm.SuperJsonProperty() }, _plugin_groups=[ orm.PluginGroup(plugins=[ Context(), Read(), RulePrepare(cfg={'skip_user_roles': True}), RuleExec(), Set(cfg={'d': { 'output.entity': '_addresses' }}) ]) ]) ] @classmethod def prepare_key(cls, input, **kwargs): user_key = input.get('user') return cls.build_key(user_key._id_str, parent=user_key)
def execute_create_order_journal(self): config_input = self.config.next_operation_input domain_key = config_input.get('domain_key') namespace = domain_key.urlsafe() models = self.context.models Journal = models['49'] Action = models['84'] PluginGroup = models['85'] CartInit = models['99'] PayPalPayment = models['108'] LinesInit = models['100'] AddressRule = models['107'] ProductToLine = models['101'] ProductSubtotalCalculate = models['104'] TaxSubtotalCalculate = models['110'] OrderTotalCalculate = models['105'] RulePrepare = models['93'] TransactionWrite = models['114'] CallbackNotify = models['115'] CallbackExec = models['97'] Unit = models['19'] entity = Journal(namespace=namespace, id='system_sales_order') entity.name = 'Sales Order Journal' entity.state = 'active' # this two need to be ordered Dictionaries because using unordered dict will cause # weird behaviour on user interface (e.g. on each refresh different order of properties entity.entry_fields = { 'company_address': orm.SuperLocalStructuredProperty('68', '7', required=True), 'party': orm.SuperKeyProperty('8', kind='0', required=True, indexed=False), # @todo buyer_reference ?? 'billing_address_reference': orm.SuperKeyProperty('9', kind='9', required=True, indexed=False), 'shipping_address_reference': orm.SuperKeyProperty('10', kind='9', required=True, indexed=False), 'billing_address': orm.SuperLocalStructuredProperty('68', '11', required=True), 'shipping_address': orm.SuperLocalStructuredProperty('68', '12', required=True), 'currency': orm.SuperLocalStructuredProperty('19', '13', required=True), 'untaxed_amount': orm.SuperDecimalProperty('14', required=True, indexed=False), 'tax_amount': orm.SuperDecimalProperty('15', required=True, indexed=False), 'total_amount': orm.SuperDecimalProperty('16', required=True, indexed=False), 'paypal_reciever_email': orm.SuperStringProperty('17', required=True, indexed=False), 'paypal_business': orm.SuperStringProperty('18', required=True, indexed=False) } entity.line_fields = { 'description': orm.SuperTextProperty('6', required=True), 'product_reference': orm.SuperKeyProperty('7', kind='38', required=True, indexed=False), 'product_variant_signature': orm.SuperJsonProperty('8', required=True), 'product_category_complete_name': orm.SuperTextProperty('9', required=True), 'product_category_reference': orm.SuperKeyProperty('10', kind='17', required=True, indexed=False), 'code': orm.SuperStringProperty('11', required=True, indexed=False), 'unit_price': orm.SuperDecimalProperty('12', required=True, indexed=False), 'product_uom': orm.SuperLocalStructuredProperty('19', '13', required=True), 'quantity': orm.SuperDecimalProperty('14', required=True, indexed=False), 'discount': orm.SuperDecimalProperty('15', required=True, indexed=False), 'taxes': orm.SuperLocalStructuredProperty('116', '16', required=True), 'subtotal': orm.SuperDecimalProperty('17', required=True, indexed=False), 'discount_subtotal': orm.SuperDecimalProperty('18', required=True, indexed=False) } entity._use_rule_engine = False entity.write() entity._transaction_actions = [ Action(key=Action.build_key('add_to_cart', parent=entity.key), name='Add to Cart', active=True, arguments={ 'domain': orm.SuperKeyProperty(kind='6', required=True), 'product': orm.SuperKeyProperty(kind='38', required=True), 'variant_signature': orm.SuperJsonProperty() }) # Other actions: update, checkout, cancel, pay, timeout, complete, message ] entity._transaction_plugin_groups = [ PluginGroup(name='Entry Init', active=True, sequence=0, transactional=False, subscriptions=[ Action.build_key('add_to_cart', parent=entity.key) ], plugins=[CartInit()]), PluginGroup(name='Payment Services Configuration', active=True, sequence=1, transactional=False, subscriptions=[ Action.build_key('add_to_cart', parent=entity.key) ], plugins=[ PayPalPayment( currency=Unit.build_key('usd'), reciever_email='*****@*****.**', business='*****@*****.**') ]), PluginGroup(name='Entry Lines Init', active=True, sequence=2, transactional=False, subscriptions=[ Action.build_key('add_to_cart', parent=entity.key) ], plugins=[LinesInit()]), PluginGroup(name='Address Exclusions, Taxes, Carriers...', active=True, sequence=3, transactional=False, subscriptions=[ Action.build_key('add_to_cart', parent=entity.key) ], plugins=[]), PluginGroup( name='Calculating Algorithms', active=True, sequence=4, transactional=False, subscriptions=[ Action.build_key('add_to_cart', parent=entity.key) ], plugins=[ AddressRule( exclusion=False, address_type='billing' ), # @todo For now we setup default address rules for both, billing & shipping addresses. AddressRule( exclusion=False, address_type='shipping' ), # @todo For now we setup default address rules for both, billing & shipping addresses. ProductToLine(), ProductSubtotalCalculate(), TaxSubtotalCalculate(), OrderTotalCalculate() ]), PluginGroup(name='Commit Transaction Plugins', active=True, sequence=5, transactional=True, subscriptions=[ Action.build_key('add_to_cart', parent=entity.key) ], plugins=[ RulePrepare(cfg={'path': '_group._entries'}), TransactionWrite(), CallbackNotify(), CallbackExec() ]), ] entity.write({ 'agent': self.context.user.key, 'action': self.context.action.key }) self.config.next_operation = 'complete' self.config.next_operation_input = {'domain_key': domain_key} self.config.write()