コード例 #1
0
ファイル: todo.py プロジェクト: kinghaoYPGE/career
class Todo(db.Document):
    desc = db.StringField()
    create_at = db.DateTimeField(default=datetime.now())
    finished_at = db.DateTimeField(default=None)  # todo的完成时间
    is_finished = db.BooleanField(default=False)  # todo状态:1. finished, 2. open
    creator = db.ReferenceField('User')
    todolist = db.ReferenceField('TodoList')

    @property
    def id(self):
        return str(self._id)

    @property
    def status(self):
        return 'finished' if self.is_finished else 'open'

    def finished(self):
        self.finished_at = datetime.now()
        self.is_finished = True
        self.save()

    def reopen(self):
        self.finished_at = None
        self.is_finished = False
        self.save()

    def to_dict(self):
        return {
            'description': self.desc,
            'creator': self.creator.username,
            'create_at': self.create_at,
            'finished_at': self.finished_at,
            'status': self.status
        }
コード例 #2
0
class OrderEntry(BaseEntry):
    meta = {
        'db_alias': 'order_db',
    }

    _item_snapshot = db.ReferenceField(
        'ItemSnapshot')  # store the info of the item when an order is produced
    _item_spec_snapshot = db.ReferenceField('ItemSpecSnapshot')

    remark = db.StringField()
    shipping_info = db.DictField()

    @property
    def item_snapshot(self):
        return self._item_snapshot or self.item

    @property
    def item_spec_snapshot(self):
        return self._item_spec_snapshot or self.spec

    @property
    def item_changed(self):
        if self.item_spec_snapshot and self.item_snapshot:
            return self.item_snapshot.is_changed or self.item_spec_snapshot.is_changed
        else:
            return False

    def create_snapshot(self, item=None, spec=None):
        if not spec:
            spec = self.spec
        if not item:
            item = self.spec.item
        from application import models as Models

        if not self._item_snapshot:
            item_snapshot = Models.ItemSnapshot.create(item)
            self._item_snapshot = item_snapshot

            self._item_snapshot.price = item.price
            self._item_snapshot.save()

        if not self._item_spec_snapshot:
            item_spec_snapshot = Models.ItemSpecSnapshot.create(
                spec, item_snapshot)
            self._item_spec_snapshot = item_spec_snapshot

            self._item_spec_snapshot.price = spec.price
            self._item_spec_snapshot.save()

        self.save()
        return self._item_snapshot, self._item_spec_snapshot

    def update_snapshot(self):
        if not (self._item_spec_snapshot and self._item_snapshot):
            return self.create_snapshot()

        self.item_snapshot.update_to_head()
        self.item_spec_snapshot.update_to_head()
        return self.save()
コード例 #3
0
class BaseEntry(db.Document):
    meta = {'allow_inheritance': True}

    spec = db.ReferenceField('ItemSpec')
    item = db.ReferenceField('Item')

    amount_usd = db.FloatField(default=0)
    amount = db.FloatField(default=0)  #CNY

    quantity = db.IntField(default=1, required=True)
    unit_price = db.FloatField(default=0)

    # after discount
    discount = db.ListField(db.DictField())

    modified = db.DateTimeField()
    created_at = db.DateTimeField(default=datetime.utcnow)

    @property
    def is_available(self):
        return (self.spec.availability and self.item.availability)

    def __unicode__(self):
        return '%s' % self.id

    def __repr__(self):
        # __repr__ method can be used by flask-cache
        return '{}({}:{})'.format(self.__class__.__name__,
                                  self.item_spec_snapshot.sku, self.quantity)

    def update_amount(self):
        from application.models import ForexRate
        self.unit_price = self.item_spec_snapshot.price
        unit_price_cny = self.unit_price * ForexRate.get()
        self.amount_usd = self.unit_price * self.quantity
        self.amount = unit_price_cny * self.quantity
        self.save()

    def to_json(self, snapshot=False):
        item = self.item_snapshot
        spec = self.item_spec_snapshot
        item_json = item.to_simple_json()
        return dict(id=str(self.id),
                    item=item_json,
                    spec=spec.to_json(),
                    unit_price=self.unit_price,
                    amount=self.amount,
                    quantity=self.quantity,
                    weight=item.weight)

    def clean(self):
        if self.spec and not self.item:
            self.item = self.spec.item
コード例 #4
0
ファイル: todo.py プロジェクト: kinghaoYPGE/career
class TodoList(db.Document):
    title = db.StringField()
    created_at = db.DateTimeField(default=datetime.now())
    creator = db.ReferenceField('User')
    todos = db.ListField(default=[])

    @property
    def id(self):
        return str(self._id)

    @property
    def todo_count(self):
        return len(self.todos)

    @property
    def finished_count(self):
        return len(list(filter(lambda x: x.is_finished, self.todos)))

    @property
    def open_count(self):
        return len(list(filter(lambda x: not x.is_finished, self.todos)))

    def to_dict(self):
        return {
            'title': self.title,
            'create_at': self.created_at,
            'creator': self.creator.username,
            'todo_count': self.todo_count,
            'finished_count': self.finished_count,
            'open_count': self.open_count
        }
コード例 #5
0
ファイル: permission.py プロジェクト: a905333282/auflux2_18
class BackendPermission(db.Document):
    meta = {
        'indexes': ['name'],
    }

    name = db.StringField(required=True, unique=True)
    roles = db.ListField(db.ReferenceField('Role'))
コード例 #6
0
class PostLike(db.Document):
    meta = {
        'db_alias': 'content_db',
        'indexes': ['user_id', 'post', 'created_at'],
        'ordering': ['-created_at']
    }

    user_id = db.ObjectIdField()
    post = db.ReferenceField('Post')
    created_at = db.DateTimeField(default=datetime.utcnow, required=True)

    @property
    def user(self):
        from application import models as Models
        return Models.User.objects(id=self.user_id).first()

    def to_json(self):
        user = self.user
        return dict(
            id=str(self.id),
            user=dict(id=str(user.id),
                      name=user.name,
                      avatar_url=user.avatar_url,
                      avatar_thumb=user.avatar_thumb),
        )
コード例 #7
0
class Item(db.Document):
    content = db.StringField(required=True)
    created_date = db.DateTimeField()
    completed = db.BooleanField(default=False)
    completed_date = db.DateTimeField()
    created_by = db.ReferenceField('User', required=True)
    notes = db.ListField(db.StringField())
    priority = db.IntField()

    def __repr__(self):
        return "<Item: {} Content: {}>".format(str(self.id), self.content)

    def to_json(self):
        return {
            'id':
            str(self.id),
            'content':
            self.content,
            'completed':
            self.completed,
            'completed_at':
            self.completed_date.strftime("%Y-%m-%d %H:%M:%S")
            if self.completed else "",
            'created_by':
            self.created_by.name,
            'notes':
            self.notes,
            'priority':
            self.priority
        }
コード例 #8
0
class Cart(db.Document):
    meta = {
        'db_alias': 'cart_db',
        'indexes': ['user_id']
    }
    entries = db.ListField(db.ReferenceField('CartEntry'))
    logistic_free = db.FloatField()
    total_price = db.FloatField()
    user_id = db.StringField()

    def __repr__(self):
        return '<Cart: {}>'.format(self.id)

    @classmethod
    def get_cart_or_create(cls, user_id):
        try:
            cart = cls.objects.get(user_id=user_id)

        except:
            cart = cls(user_id=user_id).save()

        return cart

    def total_price(self):
        total_price = 0
        for i in self.entries:
            total_price += i.price
        self.total_price = total_price
        return total_price
コード例 #9
0
class EntrySpec(db.Document):
    meta = {
        'db_alias': 'cart_db',
        'indexes': ['sku', 'item_id', ]
    }

    sku = db.IntField(required=True, unique=True)

    item_id = db.IntField()
    title = db.StringField()

    primary_image = db.StringField()
    item_available = db.BooleanField()

    price = db.FloatField()
    available = db.BooleanField()
    attributes = db.DictField()
    images = db.ListField(db.StringField())

    attribute_list = db.ListField(db.StringField())
    attribute_desc = db.DictField()

    brand = db.DictField()

    last_update_date = db.DateTimeField()

    carts = db.ListField(db.ReferenceField('Cart'))
    last_empty_date = db.DateTimeField()
コード例 #10
0
ファイル: coin.py プロジェクト: quanpower/xielaoban
class CoinWallet(db.Document):
    meta = {'indexes': ['user']}
    user = db.ReferenceField('User', unique=True)
    balance = db.IntField(required=True, default=0)
    holdding = db.IntField(required=True, default=0)
    cash = db.IntField(required=True, default=0)
    holdding_cash = db.IntField(required=True, default=0)
    latest_expired_time = db.DateTimeField(default=datetime(
        1989, 6, 4))  # latest expired_time of holdding trades

    @classmethod
    def create(cls, user, balance=0, holdding=0):
        wallet = cls.objects(user=user).modify(upsert=True,
                                               new=True,
                                               set__user=user,
                                               set__balance=balance,
                                               set__holdding=holdding)
        return wallet

    @classmethod
    def get_or_create(cls, user):
        wallet = CoinWallet.objects(user=user).first()
        if not wallet:
            wallet = CoinWallet.create(user=user)
        return wallet

    def pay(self, order, amount, coin_type=COIN_TYPE.COIN):
        if (coin_type == COIN_TYPE.COIN and amount > self.balance):
            current_app.logger.error(
                'Order coin exceed wallet balance. order: {}, amount: {}, balance: {}.'
                .format(order.id, amount, self.balance))
            amount = self.balance
        if (coin_type == COIN_TYPE.CASH and amount > self.cash):
            current_app.logger.error(
                'Order cash exceed wallet balance. order: {}, amount: {}, balance: {}.'
                .format(order.id, amount, self.cash))
            amount = self.cash

        from application import models as Models
        time = datetime.utcnow()
        reason = COIN_TRADE_REASON.PAY
        kind = COIN_TRADE_TYPE.OUTCOME
        user = Models.User.objects(id=order.customer_id).first()
        trade = CoinTrade.create(user=user,
                                 amount=amount,
                                 time=time,
                                 kind=kind,
                                 reason=reason,
                                 wallet=self,
                                 reason_id=str(order.id),
                                 coin_type=coin_type)
        return trade
コード例 #11
0
ファイル: snapshot.py プロジェクト: 8dspaces/wiwi
class ItemSnapshot(db.DynamicDocument):
    meta = {
        'db_alias': 'order_db',
        'indexes': ['item_id', 'web_id', 'head']
    }
    head = db.IntField(required=True, default=0)
    specs = db.ListField(db.ReferenceField('ItemSpecSnapshot'))
    created_at = db.DateTimeField(default=datetime.datetime.utcnow())

    def __unicode__(self):
        return '%s' % self.head

    @classmethod
    def create(cls, item):
        data = item._data
        shot = cls(**data)
        shot.head = shot.item_id
        shot.save()
        return shot

    @property
    def small_thumbnail(self):
        return self.primary_img[:23] + 'thumbnails/150x150/' + self.primary_img[23:]

    @property
    def large_thumbnail(self):
        return self.primary_img[:23] + 'thumbnails/400x400/' + self.primary_img[23:]

    def update_to_head(self):

        from application.models import Item

        head = Item.objects(item_id=self.head).first()

        if head:
            data = head._data
            for k, v in data.items():
                setattr(self, k, v)
            self.save()
        else:
            return self

    @property
    def is_changed(self):
        from application.models import Item
        head = Item.objects(item_id=self.head).first()
        if not head: return True
        if self.modified != head.modified:return True

        return False
コード例 #12
0
ファイル: order.py プロジェクト: quanpower/xielaoban
class OrderExtra(db.Document):
    meta = {
        'indexes': [
            'order', 'paid_date', 'device_id', 'client', 'version',
            'client_channel'
        ]
    }

    order = db.ReferenceField('Order', unique=True)
    paid_date = db.DateTimeField()
    client = db.StringField()
    version = db.StringField()
    device_id = db.StringField()
    client_channel = db.StringField()
コード例 #13
0
ファイル: snapshot.py プロジェクト: 8dspaces/wiwi
class ItemSpecSnapshot(db.DynamicDocument):
    meta = {
        'db_alias': 'order_db',
        'indexes': ['item_id', 'sku', 'head']
    }
    head = db.IntField(required=True, default=0)
    item = db.ReferenceField('ItemSnapshot')
    created_at = db.DateTimeField(default=datetime.datetime.utcnow())

    def __unicode__(self):
        return '%s:%s' % (self.item.head, self.head)

    @classmethod
    def create(cls, spec, itemsnapshot=None):
        if not itemsnapshot:
            itemsnapshot = ItemSnapshot.create(spec.item).save()
        data = spec._data
        shot = cls(**data)
        shot.head = shot.sku
        shot.item = itemsnapshot
        shot.save()

        itemsnapshot.specs.append(shot)
        itemsnapshot.save()
        return shot

    def update_to_head(self):
        from application.models import ItemSpec
        head = ItemSpec.objects(sku=self.head).first()

        if self.item and isinstance(self.item, ItemSnapshot):
            self.item.update_to_head()

        if head:
            data = head._data
            for k, v in data.items():
                setattr(self, k, v)
            self.save()
        else:
            return self

    @property
    def is_changed(self):
        from application.models import ItemSpec
        head = ItemSpec.objects(sku=self.head).first()
        if not head: return True
        if self.modified != head.modified: return True

        return False
コード例 #14
0
ファイル: coin.py プロジェクト: quanpower/xielaoban
class Trade(db.Document):

    meta = {
        'indexes': ['wallet', 'user', 'time', 'kind', 'reason_id', 'reason'],
        'ordering': ['wallet'],
        'allow_inheritance': True,
    }
    user = db.ReferenceField('User')
    wallet = db.ReferenceField(CoinWallet)
    amount = db.IntField(required=True)
    coin_type = db.StringField(choices=COIN_TYPE, default=COIN_TYPE.COIN)
    time = db.DateTimeField(required=True)
    kind = db.StringField(required=True, choices=tuple(COIN_TRADE_TYPE))
    reason = db.StringField(required=True, choices=tuple(COIN_TRADE_REASON))
    reason_id = db.StringField()
    description = db.StringField()
    is_hold = db.BooleanField()

    descs = {
        'PAY': u'购买',
        'CANCEL': u'取消订单',
        'SHIPPING_FEE': u'返还运费',
        'PROMOTE': u'参与活动',
        'WECHAT_LOGIN': u'微信账号登陆',
        'IOS_APP': u'下载手机客户端',
        'VERIFIED_ID': u'上传身份证资料',
        'SHARED': u'分享了一个链接',
        'SECOND_SHARED': u'你的链接被分享了',
        'SHARED_ORDER': u'通过你的链接购买',
        'ORDER': u'订单完成',
        'OTHER': u'其他',
    }

    def clean(self):
        if not self.description:
            self.description = self.descs.get(self.reason, '')
コード例 #15
0
class PostFeedback(db.Document):
    meta = {
        'db_alias': 'content_db',
        'indexes': ['user_id', 'post', 'subject', 'created_at'],
        'ordering': ['created_at']
    }

    user_id = db.ObjectIdField()
    post = db.ReferenceField('Post')
    subject = db.StringField()
    status = db.StringField(default=ACTIVITY_STATUS.PENDING,
                            choices=ACTIVITY_STATUS)
    created_at = db.DateTimeField(default=datetime.utcnow, required=True)

    @property
    def user(self):
        from application import models as Models
        return Models.User.objects(id=self.user_id).first()
コード例 #16
0
class PostActivity(db.Document):
    meta = {
        'db_alias': 'content_db',
        'indexes': ['user_id', 'to_user_id', 'post', 'created_at', 'action'],
        'ordering': ['-created_at']
    }

    user_id = db.ObjectIdField()
    post = db.ReferenceField('Post')
    action = db.StringField(choices=NOTI_TYPE)
    created_at = db.DateTimeField(default=datetime.utcnow, required=True)
    to_user_id = db.ObjectIdField()
    info = db.StringField(default="")

    @property
    def user(self):
        from application import models as Models
        return Models.User.objects(id=self.user_id).first()

    @property
    def to_user(self):
        from application import models as Models
        return Models.User.objects(id=self.to_user_id).first()

    def to_json(self):
        user = self.user
        return dict(
            id=str(self.id),
            user=dict(id=str(user.id),
                      name=user.name,
                      avatar_url=user.avatar_url,
                      avatar_thumb=user.avatar_thumb),
        )

    @classmethod
    def create(cls, user, to_user, post, action, info=''):
        noti = cls(user_id=user,
                   to_user_id=to_user,
                   action=action,
                   info=info,
                   post=post).save()
        return noti
コード例 #17
0
class OrderStat(db.Document):
    meta = {'db_alias': 'order_db', 'indexes': ['user_id']}
    user_id = db.ObjectIdField(required=True)
    orders = db.ListField(db.ReferenceField('Order'))
    items = db.ListField(db.IntField())

    total = db.FloatField(default=0)
    received = db.FloatField(default=0)

    num_orders = db.IntField(default=0)
    num_unpaid = db.IntField(default=0)
    num_waiting = db.IntField(default=0)

    def clean(self):
        for field in ('total', 'received', 'num_orders', 'num_unpaid',
                      'num_waiting'):
            if getattr(self, field, 0) < 0:
                setattr(self, field, 0)

    @classmethod
    def by_user(cls, user_id):
        cls.objects(user_id=user_id).update_one(set__user_id=user_id,
                                                upsert=True)
        return cls.objects(user_id=user_id).first()
コード例 #18
0
class User(db.Document):
    name = db.StringField()
    password = db.StringField()
    email = db.StringField()
    role = db.ReferenceField('Role')

    @property
    def id(self):
        return str(self._id)

    def to_json(self):
        return {"name": self.name, "email": self.email, "role": self.role.name}

    def is_authenticated(self):
        return True

    def is_active(self):
        return True

    def is_anonymous(self):
        return False

    def get_id(self):
        return str(self.id)
コード例 #19
0
ファイル: order.py プロジェクト: quanpower/xielaoban
class Order(db.Document):
    meta = {
        'db_alias':
        'order_db',
        'ordering': ['-created_at'],
        'indexes': [
            'customer_id', 'short_id', 'created_at', 'status', 'address',
            'amount', 'final', 'order_type', 'is_paid', 'is_payment_abnormal',
            'refund_entries'
        ]
    }
    created_at = db.DateTimeField(default=datetime.datetime.utcnow,
                                  required=True)

    order_type = db.StringField(choices=ORDER_TYPE,
                                default=ORDER_TYPE.COMMODITY)
    expired_in = db.IntField(default=1440)  # in minutes
    payment_expired_in = db.IntField(
        default=1440)  # minutes before payment should expire
    short_id = db.SequenceField(required=True, unique=True)
    is_vip = db.BooleanField(default=False)
    status = db.StringField(max_length=255,
                            required=True,
                            choices=ORDER_STATUS,
                            default=ORDER_STATUS.PAYMENT_PENDING)
    status_modified = db.DateTimeField()
    source = db.StringField(choices=ORDER_SOURCES)
    is_rewards_given = db.BooleanField(default=False)

    # order detail
    amount_usd = db.FloatField(default=0)
    amount = db.FloatField(default=0)

    discount = db.ListField(db.DictField())  # only for coupon
    coupon_codes = db.ListField(db.StringField())
    coin = db.IntField()
    hongbao = db.IntField()
    cash = db.IntField()
    final = db.FloatField(required=True)

    logistic_provider = db.StringField()

    estimated_tax = db.FloatField(default=0)
    real_tax = db.FloatField(default=-1)
    paid_tax = db.FloatField(default=-1)

    # for internal usage
    forex = db.FloatField()
    real_shipping = db.FloatField()  # real shipping fee paid
    cn_shipping = db.FloatField(default=0)

    address = db.ReferenceField('Address')
    customer_id = db.ObjectIdField(required=True)
    is_new_customer = db.BooleanField(default=False)
    entries = db.ListField(
        db.ReferenceField('OrderEntry', reverse_delete_rule=mongoengine.PULL))
    extra = db.StringField()

    # keep old property it here for migration
    logistics = db.ListField(db.ReferenceField('Logistic'))
    closed_logistics = db.ListField(db.ReferenceField('Logistic'))

    # this doesn't indicate whether customer have paid tax or not
    is_paid = db.BooleanField(default=False)
    is_payment_abnormal = db.BooleanField(default=False)
    paid_date = db.DateTimeField()
    pay_tax_deadline = db.DateTimeField()

    # informations of refundation
    refund_entries = db.ListField(
        db.ReferenceField('OrderEntry', reverse_delete_rule=mongoengine.PULL))
    refund_amount = db.FloatField(default=0)
    is_test = db.BooleanField(default=False)

    fields_to_log = {
        'status',
        'amount',
        'coin',
        'final',
        'estimated_tax',
        'real_tax',
        'paid_tax',
        'real_shipping',
        'is_paid',
    }

    PROCESSING_STATUS = [ORDER_STATUS.PAYMENT_RECEIVED, ORDER_STATUS.SHIPPING]

    ABNORMAL_STATUS = [
        ORDER_STATUS.CANCELLED, ORDER_STATUS.ABNORMAL,
        ORDER_STATUS.ORDER_DELETED, ORDER_STATUS.EXPIRED, ORDER_STATUS.REFUNDED
    ]

    def __unicode__(self):
        return '%s' % self.sid

    @classmethod
    def get_order_or_404(cls, order_id, check_user=True):
        try:
            order = cls.objects(id=order_id).first_or_404()
        except mongoengine.ValidationError:
            try:
                short_id = int(order_id)
            except (ValueError, TypeError):
                abort(404)
            order = cls.objects(short_id=short_id).first_or_404()

        if check_user and str(order.customer_id) != str(current_user.id):
            abort(404)

        return order

    @queryset_manager
    def commodities(doc_cls, queryset):
        return queryset.filter(order_type=ORDER_TYPE.COMMODITY,
                               status__nin=doc_cls.ABNORMAL_STATUS)

    @queryset_manager
    def transfer(doc_cls, queryset):
        return queryset.filter(order_type=ORDER_TYPE.TRANSFER,
                               status__nin=doc_cls.ABNORMAL_STATUS)

    @queryset_manager
    def processing(doc_cls, queryset):
        return queryset.filter(status__in=doc_cls.PROCESSING_STATUS)

    @queryset_manager
    def payment_pending(doc_cls, queryset):
        return queryset.filter(status=ORDER_STATUS.PAYMENT_PENDING)

    @queryset_manager
    def abnormal(doc_cls, queryset):
        ''' Define abnormal: status is abnormal, or partial entries are refunded.'''
        return queryset.filter(
            Q(status__in=doc_cls.ABNORMAL_STATUS) | \
            (Q(refund_entries__0__exists=True) & Q(status__in=(doc_cls.PROCESSING_STATUS+[ORDER_STATUS.RECEIVED])))
        )

    @queryset_manager
    def received(doc_cls, queryset):
        return queryset.filter(status=ORDER_STATUS.RECEIVED)

    def is_processing(self):
        return self.status in self.PROCESSING_STATUS

    def is_payment_pending(self):
        return self.status == ORDER_STATUS.PAYMENT_PENDING

    def is_abnormal(self):
        if self.status in self.ABNORMAL_STATUS:
            return True
        if self.status in self.PROCESSING_STATUS or self.status == ORDER_STATUS.RECEIVED:
            return len(self.refund_entries) > 0
        return False

    def has_refund_entries(self):
        if self.status in (self.PROCESSING_STATUS +
                           [ORDER_STATUS.RECEIVED, ORDER_STATUS.REFUNDED]):
            return len(self.refund_entries) > 0
        return False

    @property
    def tax(self):
        if self.real_tax == -1:
            return self.estimated_tax
        else:
            return self.real_tax

    @property
    def shipping(self):
        return self.cn_shipping

    @property
    def estimated_weight(self):
        return sum(
            float(entry.item_snapshot.weight) * entry.quantity
            for entry in self.entries)

    @property
    def pay_tax_remain_days(self):
        if self.pay_tax_deadline:
            time_remain = self.pay_tax_deadline.date() - datetime.date.today()
            if time_remain.days > 0:
                return time_remain.days

    @property
    def latest_logistic(self):
        from application.models import LogisticDetail
        attr = LogisticDetail.attr_by_log_stat.get(self.status)
        if not attr:
            return None
        return max(self.logistics, key=lambda l: getattr(l.detail, attr))

    @classmethod
    def create_transfer(cls,
                        customer_id,
                        entries,
                        logistic_provider,
                        coupon_codes,
                        coin=0,
                        cash=0,
                        address=None,
                        **kwargs):
        from application.models import ForexRate

        order = cls(customer_id=customer_id,
                    entries=entries,
                    logistic_provider=logistic_provider,
                    coupon_codes=coupon_codes,
                    coin=coin,
                    cash=cash,
                    **kwargs)

        if not order.forex:
            order.forex = ForexRate.get()

        order.update_amount()

        order.reload()

        if address:
            order.set_address(address)

        import application.services.jobs as Jobs
        #Jobs.stat.update_user_stats(str(order.customer_id))
        Signals.order_created.send('system', order=order)

        return order

    @classmethod
    def create_from_skus(cls,
                         customer_id,
                         skus,
                         logistic_provider,
                         coupon_codes,
                         coin=0,
                         cash=0,
                         address=None,
                         **kwargs):
        from application.models import OrderEntry, ForexRate, Item, ItemSpec

        entries = []
        for e in skus:
            availability = check_availability_and_update_stock(
                e['item_id'], e['sku'], e['quantity'])
            if not availability:
                return e
            spec = ItemSpec.objects(sku=e['sku']).first()
            item = Item.objects(item_id=e['item_id']).first()

            entry = OrderEntry(spec=spec, item=item,
                               quantity=e['quantity']).save()
            entries.append(entry)

        order = cls(customer_id=customer_id,
                    entries=entries,
                    logistic_provider=logistic_provider,
                    coupon_codes=coupon_codes,
                    coin=coin,
                    cash=cash,
                    **kwargs)

        if not order.forex:
            order.forex = ForexRate.get()

        order.update_amount()

        order.reload()
        for e in order.entries:
            e.create_snapshot()

        if address:
            order.set_address(address)

        import application.services.jobs as Jobs
        #Jobs.stat.update_user_stats(str(order.customer_id))
        Signals.order_created.send('system', order=order)

        return order

    @classmethod
    def create(cls,
               customer_id,
               entries,
               logistic_provider,
               coupon_codes,
               coin=0,
               cash=0,
               address=None,
               **kwargs):
        import application.models as Models

        order_entries = []
        for entry in entries:
            availability = check_availability_and_update_stock(
                entry.item_snapshot.item_id, entry.item_spec_snapshot.sku,
                entry.quantity)
            if not availability:
                return entry
            if isinstance(entry, (Models.CartEntry, Models.OrderEntry)):
                e = deepcopy(entry)
                e.__class__ = Models.OrderEntry
                e.id = None
                order_entries.append(e.save())
                # entry.delete()

        order = cls(customer_id=customer_id,
                    entries=order_entries,
                    logistic_provider=logistic_provider,
                    coupon_codes=coupon_codes,
                    coin=coin,
                    cash=cash,
                    **kwargs)

        if not order.forex:
            order.forex = Models.ForexRate.get()

        order.update_amount()

        order.reload()
        for e in order.entries:
            e.create_snapshot()

        if address:
            order.set_address(address)

        import application.services.jobs as Jobs
        #Jobs.stat.update_user_stats(str(order.customer_id))
        Signals.order_created.send('system', order=order)

        return order

    @property
    def item_changed(self):
        res = False
        for e in entries:
            res = res and e.item_changed
            if res: return res
        return res

    def __get__(self, *args, **kwargs):
        order = super(Order, self).__get__(*args, **kwargs)
        if (not order.is_paid) and order.item_changed: order.update_entry()
        return order

    def update_entry(self):
        if self.is_paid: return
        map(lambda e: e.update_snapshot(), self.entries)
        self.update_amount()

    def set_address(self, addr):
        ''' Create a snapshot of `addr` and make it the address of `self`.
            It's important to creat a snapshot instead of referencing to the
            origin address object such that the detail of order's address can
            not be modified. '''
        from application.models import Address
        if not isinstance(addr, Address):
            addr = Address.objects(id=addr).first()
        if not addr:
            return False
        addr_snapshot = deepcopy(addr)
        addr_snapshot.id = None
        addr_snapshot.order_id = self.id
        addr_snapshot.save()

        if self.address:
            self.address.delete(w=1)
        self.address = addr_snapshot
        self.save()
        return True

    @property
    def customer(self):
        from application import models as Models
        return Models.User.objects(id=self.customer_id).first()

    def create_payment(self, ptype, trader):
        ptype = ptype.upper()
        self.update_entry()
        self.reload()
        if self.order_type != ORDER_TYPE.TRANSFER:
            is_available = self.check_entries_avaliable()
            if not is_available or self.status in [
                    'CANCELLED', 'ABNORMAL', 'ORDER_DELETED', 'EXPIRED'
            ]:
                return None

        new_payment = Payment(order=self, ptype=ptype, trader=trader).save()
        return new_payment

    def get_payment(self, ptype):
        ptype = ptype.upper()
        payments = Payment.objects(order=self,
                                   ptype=ptype).order_by('-created_at')
        paid_payments = payments.filter(status=PAYMENT_STATUS.PAID)
        if paid_payments:
            return paid_payments.first()
        else:
            return payments.first()

    @property
    def goods_payment(self):
        return self.get_payment(PAYMENT_TYPE.WITHOUT_TAX)

    @property
    def tax_payment(self):
        return self.get_payment(PAYMENT_TYPE.WITH_TAX)

    @property
    def refunds(self):
        from application.models import Refund
        return Refund.objects(order=self)

    def check_entries_avaliable(self):
        availability = all(
            map(lambda e: e.is_available or e.item_spec_snapshot.stock != -1,
                self.entries))
        if not availability:
            self.status = ORDER_STATUS.EXPIRED
            self.save()
        return availability

    def set_paid(self):

        if self.is_paid:
            return

        self.is_new_customer = not bool(
            Order.objects(customer_id=self.customer_id, is_paid=True))
        self.is_paid = True
        self.status = ORDER_STATUS.PAYMENT_RECEIVED
        self.paid_date = datetime.datetime.utcnow()
        self.save()

        from application.services.order import payment_received
        payment_received(self)

    def update_payment(self, paid_type, paid_amount, trader):
        if paid_type == PAYMENT_TYPE.WITHOUT_TAX and not self.is_paid and \
                self.status in [ORDER_STATUS.PAYMENT_PENDING, ORDER_STATUS.WAREHOUSE_IN]:
            if paid_amount == self.final and trader == PAYMENT_TRADERS.PAYPAL:
                self.set_paid()
            elif paid_amount == float("%.2f" % (self.final * self.forex)) and \
                    trader == PAYMENT_TRADERS.WEIXIN:
                self.set_paid()
            else:
                current_app.logger.error(
                    'error at updating payment. trader: {}; ptype: {}; amount:{} order id: {}'
                    .format(trader, paid_type, paid_amount, self.id))
                self.is_payment_abnormal = True
        else:
            current_app.logger.error(
                'error at updating payment. trader: {}; ptype: {}; amount:{} order id: {}'
                .format(trader, paid_type, paid_amount, self.id))
            self.is_payment_abnormal = True
        self.save()

    @property
    def coin_trades(self):
        import application.models as Models
        return Models.Trade.objects(reason_id=str(self.id))

    def update_logistic_status(self):
        if self.logistics:
            log_status = map(lambda m: m.detail.status, self.logistics)
            new_status = min(log_status, key=lambda l: LOG_STATS.index(l))
            self._change_status(new_status)

    def _change_status(self, new_status):
        from application.services.noti import noti_order
        if self.status == new_status:
            return

        self.status = new_status
        self.status_modified = datetime.datetime.utcnow()
        self.save()

        if new_status in LOG_STATS:
            noti_order(self, new_status)
            Signals.order_logistic_stat_changed.send(
                'Order.Logistic.Status.Changed',
                order=self,
                new_status=new_status)
        else:
            Signals.order_status_changed.send('order_status_changed',
                                              order=self,
                                              new_status=new_status)

    def delete_order(self):
        for mo in self.logistics:
            mo.delete(w=1)

        for entry in self.entries:
            entry.delete(w=1)

        import application.services.jobs as Jobs
        #Jobs.stat.update_user_stats(str(self.customer_id))

        if self.goods_payment:
            self.goods_payment.delete(w=1)
        self.delete(w=1)

    def cancel_order(self, reason, status=None):
        for mo in self.logistics:
            mo.close(reason)
        self.extra = reason
        self.save()

        if not status:
            status = ORDER_STATUS.ABNORMAL
        self._change_status(status)
        for entry in self.entries:
            try:
                if entry.spec.stock != -1:
                    entry.spec.update(inc__stock=entry.quantity,
                                      set__availability=True)
                    entry.item.update(set__availability=True,
                                      set__status='MOD')
            except AttributeError:
                pass

    def update_amount(self):
        from application.services.price import cal_order_price_and_apply, \
            cal_order_tax

        for e in self.entries:
            e.update_amount()

        cal_order_price_and_apply(self)

        self.estimated_tax = cal_order_tax(self)
        self.save()

    @property
    def sid(self):
        return self.short_id

    def to_json(self,
                include_logistic=False,
                replace_entries_to_refunded=False):
        if not self.is_paid:
            self.update_amount()
            self.reload()

        entries_json = []
        if replace_entries_to_refunded and self.has_refund_entries():
            for e in self.refund_entries:
                entries_json.append(e.to_json())
        else:
            for e in self.entries:
                entries_json.append(e.to_json())

        refund_entries_json = []
        for e in self.refund_entries:
            refund_entries_json.append(e.to_json())

        result = dict(
            id=str(self.id),
            short_id=str(self.sid),
            status=self.status,
            customer_id=str(self.customer_id),
            amount=self.amount,
            cn_shipping=self.cn_shipping,
            coin=self.coin,
            hongbao=self.hongbao,
            discount=self.discount,
            final=self.final,
            estimated_tax=self.estimated_tax,
            payment_status='PAID' if self.is_paid else 'UNPAID',
            payment_ref_number=[
                p.ref_number for p in Payment.objects(order=self)
            ],
            created_at=format_date(self.created_at),
            entries=entries_json,
            refund_entries=refund_entries_json,
            refund_amount=self.refund_amount,
            real_tax=self.real_tax,
        )

        if self.address:
            result.update({"address": self.address.to_json()})

        if include_logistic:
            result.update(dict(logistics=[l.to_json()
                                          for l in self.logistics]))

        return result

    def to_grouped_json(self):
        res = {
            'estimated_weight': self.estimated_weight,
            'amount': self.amount,
            'cn_shipping': self.cn_shipping,
            'coin': self.coin,
            'hongbao': self.hongbao,
            'discount': self.discount,
            'final': self.final,
            'estimated_tax': self.estimated_tax
        }

        res['sid'] = self.sid
        res['status'] = self.status
        if self.address:
            res.update(dict(address=self.address.to_json()))
        return res
コード例 #20
0
ファイル: user.py プロジェクト: a905333282/auflux1
class User(db.Document, UserMixin):
    '''
    The User class contains only basic and frequently used information


    Superclass UserMixin can provide four authenticate methods required
    by Flask-Login.
    '''
    meta = {
        'db_alias': 'user_db',
    }
    name = db.StringField(required=True)
    account = db.EmbeddedDocumentField('UserAccount')
    information = db.EmbeddedDocumentField('UserInformation')

    selling_items = db.ListField(db.StringField())
    sold_items = db.ListField(db.StringField())
    owned_item = db.ListField(db.StringField())
    bought_item = db.ListField(db.StringField())

    roles = db.ListField(db.StringField())

    # favor related (item_ids)
    num_favors = db.IntField(default=0, min_value=0)
    favor_items = db.ListField(db.StringField())

    addresses = db.ListField(db.ReferenceField('Address'))
    default_address = db.ReferenceField('Address')

    verified = db.BooleanField(default=False)
    is_deleted = db.BooleanField(default=False)
    deleted_date = db.DateTimeField()

    def __unicode__(self):
        return '%s' % str(self.id)
        #return u'{}'.format(self.name)

    @property
    def avatar_thumb(self):
        return self.avatar_url[:23] + 'avatar_thumbs/80x80/' + self.avatar_url[23:]

    @db.queryset_manager
    def active(doc_cls, queryset):
        return queryset.filter(is_deleted=False)

    @property
    def is_admin(self):
        return USER_ROLE.ADMIN in self.roles

    @classmethod
    def is_verified(self):
        return self.verified

    def verify(self):
        self.verified = True
        self.save()
        return True

    def to_json(self):
        data = dict(name=self.name,
                    created_at=str(self.account.created_at),
                    id=str(self.id)
                )
        return data

    @classmethod
    def authenticate(cls, mobile_number=None, password=None):
        if mobile_number:
            user = cls.active(account__mobile_number=mobile_number).first()
        else:
            user = None
        if user:
            authenticated = user.account.check_password(password)
        else:
            authenticated = False

        return user, authenticated

    def generate_auth_token(self, expires_in=604800):
        s = Serializer(current_app.config['SECRET_KEY'], expires_in=expires_in)
        return s.dumps({'id': str(self.id)}).decode('utf-8')

    @staticmethod
    def verify_auth_token(token):
        s = Serializer(current_app.config['SECRET_KEY'])
        try:
            data = s.loads(token)
        except:
            return None
        return User.objects(id=data['id']).first()


    @classmethod
    def create(cls, mobile_number, password, name, email=None):


        # account
        account = UserAccount(mobile_number=mobile_number,
                              email=email,
                              is_email_verified=True)
        account.password = password

        user = User(name=name,
                    roles=[USER_ROLE.MEMBER],
                    information=UserInformation(),
                    account=account)

        user.save()

        signals.user_signup.send('system', user=user)
        return user
コード例 #21
0
class User(db.Document, UserMixin):
    '''
    The User class contains only basic and frequently used information


    Superclass UserMixin can provide four authenticate methods required
    by Flask-Login.
    '''
    meta = {
        'db_alias': 'user_db',
    }
    name = db.StringField(required=True)
    account = db.EmbeddedDocumentField('UserAccount')
    information = db.EmbeddedDocumentField('UserInformation')

    avatar_url = db.URLField(default='http://assets.maybi.cn/logo/panda.jpg')

    # level
    # 0: normal user
    # 1: normal member; 2: advance member
    # 3: premium member; 4: VIP member
    level = db.IntField(default=0)
    roles = db.ListField(db.StringField())

    # whether subscribed our wechat account
    subscribed_mp = db.BooleanField(default=False)

    # favor related (item_ids)
    num_favors = db.IntField(default=0, min_value=0)
    favor_items = db.ListField(db.IntField())

    addresses = db.ListField(db.ReferenceField('Address'))
    default_address = db.ReferenceField('Address')

    # favor related (post_ids)
    num_post_likes = db.IntField(default=0, min_value=0)
    like_posts = db.ListField(db.IntField())

    is_deleted = db.BooleanField(default=False)
    deleted_date = db.DateTimeField()

    def __unicode__(self):
        return '%s' % str(self.id)
        #return u'{}'.format(self.name)

    @property
    def avatar_thumb(self):
        return self.avatar_url[:23] + 'avatar_thumbs/80x80/' + self.avatar_url[
            23:]

    @db.queryset_manager
    def active(doc_cls, queryset):
        return queryset.filter(is_deleted=False)

    @property
    def is_admin(self):
        return USER_ROLE.ADMIN in self.roles

    def to_json(self):
        data = dict(name=self.name,
                    avatar_url=self.avatar_url,
                    avatar_thumb=self.avatar_thumb,
                    num_followers=self.num_followers,
                    num_followings=self.num_followings,
                    created_at=str(self.account.created_at),
                    id=str(self.id))
        return data

    @classmethod
    def authenticate(cls, email=None, password=None):
        if email:
            user = cls.active(account__email=email.lower()).first()
        else:
            user = None
        if user:
            authenticated = user.account.check_password(password)
        else:
            authenticated = False

        return user, authenticated

    def generate_auth_token(self, expires_in=604800):
        s = Serializer(current_app.config['SECRET_KEY'], expires_in=expires_in)
        return s.dumps({'id': str(self.id)}).decode('utf-8')

    @staticmethod
    def verify_auth_token(token):
        s = Serializer(current_app.config['SECRET_KEY'])
        try:
            data = s.loads(token)
        except:
            return None
        return User.objects(id=data['id']).first()

    @classmethod
    def create(cls, email, password, name, mobile_number=None):

        # account
        account = UserAccount(email=email.lower(),
                              mobile_number=mobile_number,
                              is_email_verified=True)
        account.password = password

        user = User(name=name,
                    roles=[USER_ROLE.MEMBER],
                    information=UserInformation(),
                    account=account)

        user.save()

        signals.user_signup.send('system', user=user)
        return user
コード例 #22
0
class SocialOAuth(db.Document):
    meta = {'indexes': ['site', 'user', ('site_uid', 'site'), 'unionid']}

    app = db.StringField(choices=['IOS', 'MOBILEWEB'], default='MOBILEWEB')
    site = db.StringField(max_length=255, required=True)
    site_uid = db.StringField(max_length=255,
                              required=True,
                              unique_with='site')
    unionid = db.StringField()
    user = db.ReferenceField('User')

    site_uname = db.StringField(max_length=255)
    access_token = db.StringField(required=True)
    expire_date = db.DateTimeField()
    refresh_token = db.StringField()

    # wether we can get information of this oauth
    can_refresh = db.BooleanField(default=True)

    last_active_date = db.DateTimeField()

    def to_json(self):
        return dict(site=self.site, site_uname=self.site_uname)

    @classmethod
    def create(cls,
               site,
               site_uid,
               site_uname,
               access_token,
               expires_in=0,
               refresh_token=None,
               email=None,
               mobile_number=None,
               gender=None,
               password=None,
               unionid=None,
               app='MOBILEWEB',
               is_email_verified=False):
        """ create an oauth record and an user"""
        oauth = cls(site=site,
                    site_uid=site_uid,
                    site_uname=site_uname,
                    access_token=access_token,
                    refresh_token=refresh_token,
                    unionid=unionid,
                    app=app)

        if not email:
            email = '{}-{}@maybi.cn'.format(
                site,
                hashlib.md5(
                    (app + site + site_uid).encode('utf-8')).hexdigest())

        # create an user
        user = User.create(email=email,
                           mobile_number=mobile_number,
                           password=password or site_uname,
                           name=site_uname)
        user.account.is_email_verified = is_email_verified
        user.information.gender = gender
        if site == 'wechat':
            user.subscribed_mp = True
        user.save()

        oauth.user = user
        oauth.save()
        oauth.update_token(access_token, expires_in)
        return oauth

    def update_token(self, access_token, expires_in=0):
        expire_date = datetime.datetime.utcnow() + datetime.timedelta(
            seconds=int(expires_in))
        self.update(set__access_token=access_token,
                    set__expire_date=expire_date)

    def re_auth(self,
                access_token,
                expires_in,
                refresh_token=None,
                unionid=None):
        self.update_token(access_token, expires_in)
        self.update(set__refresh_token=refresh_token)
        if unionid:
            self.update(set__unionid=unionid)

    def update_avatar(self, url):
        self.user.update(set__avatar_url=url)

    @classmethod
    def get_user(cls, site, site_uid):
        so = cls.objects(site=site, site_uid=site_uid).first()
        return so.user if so else None

    @classmethod
    def refresh_active(cls, site, site_uid, dt):
        # ignore if document does not exist
        cls.objects(site=site,
                    site_uid=site_uid).update_one(set__last_active_date=dt)
コード例 #23
0
class User(db.Document, UserMixin, FavorAction):
    '''
    The User class contains only basic and frequently used information


    Superclass UserMixin can provide four authenticate methods required
    by Flask-Login.
    '''
    meta = {
        'indexes': [
            'name', 'account.created_at', 'roles', 'level', 'account.email',
            'account.is_email_verified', 'is_deleted'
        ],
        'ordering': ['-account.created_at']
    }

    name = db.StringField(required=True)
    account = db.EmbeddedDocumentField('UserAccount')
    information = db.EmbeddedDocumentField('UserInformation')

    avatar_url = db.URLField(default='http://assets.maybi.cn/logo/panda.jpg')

    # level
    # 0: normal user
    # 1: normal member; 2: advance member
    # 3: premium member; 4: VIP member
    level = db.IntField(default=0)
    roles = db.ListField(db.StringField())

    addresses = db.ListField(db.ReferenceField('Address'))
    default_address = db.ReferenceField('Address')

    # followers
    num_followers = db.IntField(default=0, min_value=0)
    num_followings = db.IntField(default=0, min_value=0)
    followers = db.ListField(db.ReferenceField('User'))
    followings = db.ListField(db.ReferenceField('User'))

    # whether subscribed our wechat account
    subscribed_mp = db.BooleanField(default=False)

    # favor related (item_ids)
    num_favors = db.IntField(default=0, min_value=0)
    favor_items = db.ListField(db.IntField())

    # favor related (post_ids)
    num_post_likes = db.IntField(default=0, min_value=0)
    like_posts = db.ListField(db.IntField())

    # shopping cart
    cart = db.ReferenceField('Cart')

    # wallet
    wallet = db.ReferenceField('CouponWallet')

    is_deleted = db.BooleanField(default=False)
    deleted_date = db.DateTimeField()

    def __unicode__(self):
        return '%s' % str(self.id)
        #return u'{}'.format(self.name)

    @property
    def coin_wallet(self):
        import application.models as Models
        return Models.CoinWallet.get_or_create(user=self)

    @property
    def hongbao_wallet(self):
        import application.models as Models
        return Models.HongbaoWallet.by_user(user=self)

    @property
    def orders(self):
        import application.models as Models
        return Models.Order.objects(customer_id=self.id, is_paid=True)

    @property
    def avatar_thumb(self):
        return self.avatar_url[:23] + 'avatar_thumbs/80x80/' + self.avatar_url[
            23:]

    def used_coupon(self, code):
        import application.models as Models
        return bool(
            Models.Order.objects(customer_id=self.id,
                                 is_paid=True,
                                 coupon__codes__contains=code))

    @db.queryset_manager
    def active(doc_cls, queryset):
        return queryset.filter(is_deleted=False)

    @property
    def is_admin(self):
        return USER_ROLE.ADMIN in self.roles

    # Follow / Following
    def follow(self, other):
        if self not in other.followers:
            other.followers.append(self)
            other.num_followers += 1

        if other not in self.followings:
            self.followings.append(other)
            self.num_followings += 1
        self.save()
        other.save()

        signals.site_message.send(self,
                                  dest=other.id,
                                  source=self,
                                  imgs=[self.avatar_url],
                                  noti_type=NOTI_TYPE.FOLLOW,
                                  title='')

    def unfollow(self, other):
        if self in other.followers:
            other.followers.remove(self)
            other.num_followers -= 1

        if other in self.followings:
            self.followings.remove(other)
            self.num_followings -= 1

        self.save()
        other.save()

    def is_following(self, other):
        return other in self.followings

    def to_json(self):
        data = dict(name=self.name,
                    avatar_url=self.avatar_url,
                    avatar_thumb=self.avatar_thumb,
                    num_followers=self.num_followers,
                    num_followings=self.num_followings,
                    created_at=str(self.account.created_at),
                    id=str(self.id))
        return data

    @classmethod
    def authenticate(cls, email=None, password=None):
        if email:
            user = cls.active(account__email=email.lower()).first()
        else:
            user = None
        if user:
            authenticated = user.account.check_password(password)
        else:
            authenticated = False

        return user, authenticated

    def generate_auth_token(self, expires_in=604800):
        s = Serializer(current_app.config['SECRET_KEY'], expires_in=expires_in)
        return s.dumps({'id': str(self.id)}).decode('utf-8')

    @staticmethod
    def verify_auth_token(token):
        s = Serializer(current_app.config['SECRET_KEY'])
        try:
            data = s.loads(token)
        except:
            return None
        return User.objects(id=data['id']).first()

    @classmethod
    def create(cls, email, password, name, mobile_number=None):
        from application.models.coupon.wallet import CouponWallet
        from application.models.cart import Cart

        # init user account.
        cart = Cart()
        cart.save()
        wallet = CouponWallet()
        wallet.save()

        # account
        account = UserAccount(email=email.lower(),
                              mobile_number=mobile_number,
                              is_email_verified=True)
        account.password = password

        user = User(name=name,
                    roles=[USER_ROLE.MEMBER],
                    information=UserInformation(),
                    cart=cart,
                    wallet=wallet,
                    account=account)

        user.save()

        signals.user_signup.send('system', user=user)
        return user

    def mark_deleted(self):
        if self.is_deleted:
            return
        # delete social oauth, otherwise user can still login via wechat
        SocialOAuth.objects(user=self).delete()

        self.is_deleted = True
        self.deleted_date = datetime.datetime.utcnow()
        self.save()
コード例 #24
0
ファイル: logistic.py プロジェクト: quanpower/xielaoban
class Logistic(db.Document):
    meta = {
        'db_alias': 'order_db',
        'indexes': ['is_closed', '-created_at',
                    'order', 'entries',
                    'detail.status',
                    'detail.channel',
                    'detail.modified',
                    'detail.cn_tracking_no',
                    'detail.cn_logistic_name',
                    'detail.payment_received_date',
                    'detail.processing_date',
                    'detail.shipping_date',
                    'detail.port_arrived_date',
                    'detail.received_date',
                    'detail.partner',
                    'detail.partner_tracking_no',
                    ('detail.status', '-created_at'),
        ]
    }
    order = db.ReferenceField('Order', required=True)
    entries = db.ListField(db.ReferenceField('OrderEntry'))
    returned_entries = db.ListField(db.ReferenceField('OrderEntry'))

    is_closed = db.BooleanField(default=False, required=True)
    close_reason = db.StringField()
    closed_at = db.DateTimeField()
    created_at = db.DateTimeField(
        required=True, default=datetime.datetime.utcnow
    )
    detail = db.EmbeddedDocumentField('LogisticDetail')

    def __unicode__(self):
        return '%s' % str(self.id)

    @property
    def estimated_weight(self):
        return sum(float(entry.item_snapshot.weight) * entry.quantity
                   for entry in self.entries)

    def to_json(self):
        detail = self.detail
        return dict(cn_tracking_no=detail.cn_tracking_no,
                    cn_logistic_name=detail.cn_logistic_name,
                    partner_tracking_no=detail.partner_tracking_no,
                    partner_name=detail.partner.name if detail.partner else '',
                    payment_received_date=get_date(detail, 'payment_received_date'),
                    processing_date=get_date(detail, 'processing_date'),
                    shipping_date=get_date(detail, 'shipping_date'),
                    port_arrived_date=get_date(detail, 'port_arrived_date'),
                    received_date=get_date(detail, 'received_date'),
                    estimated_weight=self.estimated_weight,
                    real_weight=detail.real_weight,
                    status=detail.status)

    @property
    def amount(self):
        return sum(entry.amount for entry in  self.entries)

    @property
    def amount_usd(self):
        return sum(entry.amount_usd for entry in  self.entries)

    @classmethod
    def create(cls, order):
        from application.models.order.partner import Partner

        def update_order(o):
            o.logistics.append(log)
            o.save()
            o.update_logistic_status()

        def create_logistic(order):
            log = cls(detail=LogisticDetail())
            log.order = order
            log.entries = order.entries
            log.detail.partner = Partner.objects().first()
            log.detail.partner_tracking_no = gen_uid()
            log.detail.status = order.status
            date_field = log.detail.attr_by_log_stat[order.status]
            setattr(log.detail, date_field, datetime.datetime.utcnow())

            log.save()
            return log

        log = create_logistic(order)
        update_order(order)
        return log

    @classmethod
    def merge_with(cls, los):

        if not type(los) is list:
            return False

        start = 0
        for index in range(len(los)-1):
            if los[index+1].detail.cn_tracking_no != \
                    los[start].detail.cn_tracking_no or \
                    los[index+1].order != los[0].order :
                return False

        for index in range(len(los)-1):
            map(lambda e: los[index+1].entries.append(e),
                los[index].entries)
            los[index].entries = []
            los[index].save()
            los[index].close(
                'merged with %s' %
                los[index+1].id, datetime.datetime.utcnow()
            )
            los[index+1].save()

            if index+1 == len(los)-1:
                comment = LogisticRemark(
                    content=u"合并单", creator=current_user.name
                )
                los[index+1].detail.remarks.append(comment)
                los[index+1].save()
                return los[index+1]

    def close(self, reason, time=None):
        self.is_closed = True
        self.close_reason = reason
        self.closed_at = time or datetime.datetime.utcnow()
        self.save()
        order = self.order
        if self in order.logistics:
            order.logistics.remove(self)
            order.closed_logistics.append(self)
            order.save()

    def fork_by_entries(self, entry_ids):
        def get_entry(entries, eid):
            for e in entries:
                if str(eid) == str(e.id):
                    return e
            return False

        if not type(entry_ids) is list:
            return False

        if len(self.entries) < 2:
            return False

        if self.detail.status != 'PAYMENT_RECEIVED':
            self.detail.status = 'PROCESSING'
            self.detail.processing_date = datetime.datetime.utcnow()
            self.save()
        forked_order = Logistic(detail=self.detail)
        entries = [get_entry(self.entries, e) for e in entry_ids]
        if not entries:
            False
        forked_order.entries = entries
        forked_order.order = self.order
        forked_order.save()
        self.order.logistics.append(forked_order)
        self.order.save()

        map(lambda e: self.entries.remove(e), entries)
        self.save()

        old_ptn = self.detail.partner_tracking_no
        new_ptn = gen_uid()
        forked_order.detail.partner_tracking_no = new_ptn

        remark = u"%s拆出了%s" % (old_ptn, new_ptn)
        forked_order.update_remark(remark, current_user.name)
        self.update_remark(remark, current_user.name)

        return self, forked_order

    def update_status(self, new_status):
        try:
            index = LOG_STATS.index(new_status)
        except ValueError:
            return

        log = self.detail
        old_status = log.status

        if index > LOG_STATS.index(old_status):
            log.status = new_status
            now = datetime.datetime.utcnow()
            date_field = log.attr_by_log_stat[new_status]
            if not getattr(log, date_field):
                setattr(log, date_field, now)
            self.save()

            self.order.update_logistic_status()

    def update_logistic(self, info):
        log = self.detail
        next_status = info.get('status', '')

        require = {
            'SHIPPING': ['cn_tracking_no', 'cn_logistic_name'],
        }
        if next_status in require.keys():
            values = require[next_status]
            for value in values:
                if value in info:
                    continue
                value = getattr(self.detail, value)
                if value in [None, '', 'None', '?', u'?', u'None',
                             'Undefined', u'Undefined']:
                    raise Exception("Fail to update logistic")

        if current_user and not current_user.is_anonymous:
            modified_by = current_user.name
        else:
            modified_by = 'system'

        self.update_status(next_status)

        remark = info.get('remark', None)
        if remark:
            self.update_remark(remark, modified_by)

        delay_content = info.get('delay', None)
        if delay_content:
            self.update_delay(delay_content, modified_by)

        irregularity = info.get('irregularity', None)
        if irregularity:
            self.update_irregularity(irregularity, modified_by)

        for field, value in info.items():
            if field in ['status']:
                continue
            setattr(log, field, value)
        self.save()

        Signals.logistic_stat_changed.send(
            'send to logistic partner', lo=self, status=next_status)

        Signals.logistic_info_updated.send('system', logistic=self)

    def update_remark(self, content, modified_by):
        remark = LogisticRemark(content=content, creator=modified_by)
        self.detail.remarks.append(remark)
        self.save()

    def update_delay(self, content, modified_by):
        delays = self.detail.delay_details.filter(status=self.detail.status)
        if delays:
            delays.update(reason=content, creator=modified_by)
        else:
            delay = LogisticDelay(
                reason=content, creator=modified_by,
                status=self.detail.status
            )
            self.detail.delay_details.append(delay)
        remark = LogisticRemark(content=content, creator=modified_by)
        self.detail.remarks.append(remark)
        self.save()

    def update_irregularity(self, irregularity, modified_by):
        irregularity.creator = modified_by
        self.detail.irregular_details.append(irregularity)
        self.save()

    @property
    def shipping_history(self):
        res = []
        status = self.detail.status
        for st in LOG_STATS:
            field = self.detail.attr_by_log_stat[st]
            val = getattr(self.detail, field)
            if not val:
                continue
            res.append({'time': format_date(val),
                        'desc': SHIPPING_HISTORY[st],
                        'status': st})
            if st == status:
                break
        return res

    @property
    def express_tracking(self):
        import application.models as Models
        com = self.detail.cn_logistic_name
        nu = self.detail.cn_tracking_no
        if com and nu:
            tracking = Models.ExpressTracking.find(
                company=com,
                number=nu)
            return tracking
        return None

    @classmethod
    def pre_save_post_validation(cls, sender, document, **kwargs):
        if not kwargs['created']:
            old_log = cls.objects(id=document.id).first().detail
            new_log = document.detail
            for field in LogisticDetail._fields:
                if field == 'modified':
                    continue
                if (getattr(old_log, field) != getattr(new_log, field)):
                    new_log.modified = datetime.datetime.utcnow()
                    if current_user and not current_user.is_anonymous:
                        new_log.modified_by = current_user.name
                    else:
                        new_log.modified_by = 'system'
                    return
        else:
            document.detail.modified = datetime.datetime.utcnow()
コード例 #25
0
ファイル: logistic.py プロジェクト: quanpower/xielaoban
class LogisticDetail(db.EmbeddedDocument):
    partner_tracking_no = db.StringField(default='', required=True)
    cn_tracking_no = db.StringField(default='')
    cn_logistic_name = db.StringField(default='')
    carrier_tracking_no = db.StringField(default='')
    partner = db.ReferenceField('Partner')
    channel = db.StringField()
    route = db.StringField(default="DEFAULT")

    pending_review_date = db.DateTimeField()
    transfer_approved_date = db.DateTimeField()
    warehouse_in_date = db.DateTimeField()
    payment_received_date = db.DateTimeField(default=datetime.datetime.utcnow)
    processing_date = db.DateTimeField()
    shipping_date = db.DateTimeField()
    port_arrived_date = db.DateTimeField()
    received_date = db.DateTimeField()
    pending_return_date = db.DateTimeField()
    returned_date = db.DateTimeField()

    remarks = db.EmbeddedDocumentListField('LogisticRemark')
    delay_details = db.EmbeddedDocumentListField('LogisticDelay')
    irregular_details = db.EmbeddedDocumentListField('LogisticIrregular')
    extra = db.StringField(default='')

    real_weight = db.FloatField(default=0)
    real_fee = db.FloatField()

    modified = db.DateTimeField()
    modified_by = db.StringField()
    status = db.StringField(max_length=255, required=True,
                            choices=LOG_STATS,
                            default=LOG_STATS.PAYMENT_RECEIVED)
    attr_by_log_stat = {
        'PENDING_REVIEW': 'pending_review_date',
        'TRANSFER_APPROVED': 'transfer_approved_date',
        'WAREHOUSE_IN': 'warehouse_in_date',
        'PAYMENT_RECEIVED': 'payment_received_date',
        'PROCESSING': 'processing_date',
        'SHIPPING': 'shipping_date',
        'PORT_ARRIVED': 'port_arrived_date',
        'RECEIVED': 'received_date',
        'PENDING_RETURN': 'pending_return_date',
        'RETURNED': 'returned_date'
    }

    fields_to_log = {
        'cn_tracking_no', 'partner_tracking_no', 'status',
        'cn_logistic_name', 'partner', 'payment_received_date',
        'shipping_date', 'processing_date',
        'received_date', 'port_arrived_date', 'modified_by',
    }

    def next(self):
        if self.status in LOG_STATS and self.status != 'RECEIVED':
            self.status = LOG_STATS[LOG_STATS.index(self.status) + 1]

    @property
    def cn_logistic(self):
        from application.models import Express
        return Express.objects(name=self.cn_logistic_name).first()
コード例 #26
0
ファイル: order.py プロジェクト: quanpower/xielaoban
class Payment(db.Document):
    meta = {
        'db_alias': 'order_db',
        'indexes': ['order', 'ptype', '-created_at']
    }

    created_at = db.DateTimeField(default=datetime.datetime.utcnow,
                                  required=True)

    order = db.ReferenceField('Order')
    logistic = db.ReferenceField('Logistic')

    other_reason = db.StringField()
    ptype = db.StringField(required=True, choices=PAYMENT_TYPE)
    status = db.StringField(max_length=255,
                            required=True,
                            choices=PAYMENT_STATUS,
                            default=PAYMENT_STATUS.UNPAID)

    # transaction reference number from alipay/bank
    ref_number = db.StringField(max_length=100)
    paid_amount = db.FloatField()
    foreign_amount = db.FloatField()
    currency = db.StringField()
    buyer_id = db.StringField(max_length=50)
    trader = db.StringField(choices=PAYMENT_TRADERS)
    trade_type = db.StringField(choices=TRADE_TYPE)
    trade_status = db.StringField()
    trader_msg = db.StringField()
    extra = db.StringField()
    modified = db.DateTimeField()
    redirect_url = db.StringField()

    @property
    def is_paid(self):
        return self.status == PAYMENT_STATUS.PAID

    @property
    def amount(self):
        if self.ptype == PAYMENT_TYPE.WITHOUT_TAX:
            return self.order.final
        if self.ptype == PAYMENT_TYPE.WITH_TAX:
            return self.order.final + self.order.tax

    def mark_paid(self, data):
        if self.is_paid:
            return
        self.update(set__status=PAYMENT_STATUS.PAID)
        kwargs = {'set__' + key: value for key, value in data.items()}
        self.update(**kwargs)
        self.reload()

        paid_amount = float(data.get('paid_amount', 0))

        if self.ptype == PAYMENT_TYPE.WITHOUT_TAX:
            self.order.update_payment(self.ptype, paid_amount, self.trader)

    def to_json(self):
        return dict(id=self.id,
                    ref_num=self.ref_number,
                    status=self.status,
                    type=self.type,
                    amount=self.amount)