Beispiel #1
0
class ProductDelivery(db.Model, CRUDMixin):

    delivery_type = db.Column(db.String(128))
    variant_id = db.Column(db.String(255), nullable=True, index=True)
    cost = db.Column(db.Numeric(precision=18, scale=2))

    country_id = db.Column(db.Integer,
                           db.ForeignKey('countries.id'),
                           nullable=True)
    country = db.relationship('Country')

    __table_args__ = (db.UniqueConstraint('delivery_type',
                                          'variant_id',
                                          'country_id',
                                          name='uq_type_variant_country'), )
Beispiel #2
0
class OrderMixin(CRUDMixin):
    shop_id = db.Column(db.String(128), default=SHOP_ID)

    billing_city = db.Column(db.Unicode(255))
    billing_street = db.Column(db.Unicode(255))
    billing_apartment = db.Column(db.Unicode(20))
    billing_zip_code = db.Column(db.String(20))

    delivery_city = db.Column(db.Unicode(255))
    delivery_street = db.Column(db.Unicode(255))
    delivery_apartment = db.Column(db.Unicode(20))
    delivery_zip_code = db.Column(db.String(20))
    # summary cost of all cart items linked with this order
    goods_price = db.Column(db.Numeric(precision=18, scale=2))

    vat = db.Column(db.Numeric(precision=18, scale=2))
    total_price = db.Column(db.Numeric(precision=18, scale=2))

    payment_method = db.Column(db.String, nullable=False, index=True)
    state = db.Column(db.Integer, index=True)
    # stored cost for the order delivery
    #delivery_method = db.Column(db.String, nullable=False, index=True)
    delivery_price = db.Column(db.Numeric(precision=18, scale=2))

    @declared_attr
    def billing_country_id(cls):
        return db.Column(db.Integer, db.ForeignKey('countries.id',
                         use_alter=True, name='fk_billing_country'))

    @declared_attr
    def delivery_country_id(cls):
        return db.Column(db.Integer, db.ForeignKey('countries.id',
                         use_alter=True, name='fk_delivery_country'))

    @declared_attr
    def customer_id(cls):
        return db.Column(db.Integer, db.ForeignKey('customers.id'),
                         nullable=False, index=True)

    @declared_attr
    def customer(cls):
        return db.relationship('Customer',
                               backref=db.backref('orders', **lazy_cascade))

    @declared_attr
    def goods(cls):
        return db.relationship('Cart', backref='order', **lazy_cascade)

    @classmethod
    def create_from_api(cls, customer_id, **kwargs):
        """ This method should be overrided in Order model implemetation
        """
        raise NotImplementedError()

    def mark_paid(self):
        order_paid.send(current_app._get_current_object(), order=self)
        return self.update(state=OrderStates.paid)

    def resolve_payment(self, method=None):
        payment_method = self.payment_method or method
        method = current_app.config['PAYMENT_METHODS'][payment_method]
        class_string = method['module']
        PaymentMethod = import_string(class_string)
        return PaymentMethod(self)

    def set_payment_details(self, **kwargs):
        raise NotImplementedError("Payment Details: %s", kwargs)

    @classmethod
    def get_by_payment_details(cls, **kwargs):
        raise NotImplementedError("Payment Details: %s", kwargs)

    @classmethod
    def _prepare_address(cls, addr_type, address_instance):
        exclude_fields = ['customer_id', 'created_at', 'id', 'type']
        address_dict = address_instance.as_dict(exclude=exclude_fields)
        return dict(('{}_{}'.format(addr_type, key), value)
                    for key, value in address_dict.iteritems())

    @classmethod
    def _resolve_delivery(cls, delivery, address):
        return delivery.calculate_price(address)

    @classmethod
    def cancel_payment(cls, payment_method, **kwargs):
        """
        This method is called when payment is canceled by customer. Override it

        :param payment_method: string which identifies payment method
        :param kwargs: additional params passed to identify the payment
        """
        raise NotImplementedError()

    def cancel_by_merchant(self):
        """ Cancels order as if merchant decided to do so, e.g.
        when customer didn't pay in time
        """
        self._delete_carts()
        self.update(state=OrderStates.merchant_canceled)

    def _delete_carts(self):
        cart_cls = get_cart_class()
        carts = cart_cls.query.filter_by(order_id=self.id)

        # we don't use bulk delete, because there's a special Cart.delete()
        for cart in carts:
            cart.delete()

    @classmethod
    def expired(cls, max_age):
        """ Returns all order items in 'created' state with age >= max_age
            :param max_age: timedelta object
        """
        min_created_at = datetime.utcnow() - max_age
        return cls.query.filter(cls.created_at <= min_created_at,
                                cls.state == OrderStates.created)
Beispiel #3
0
class CartMixin(CRUDMixin):
    """ Cart record for concrete product
    """
    product_id = db.Column(db.String, nullable=False)
    product_variant_id = db.Column(db.String, nullable=False)
    price_option_id = db.Column(db.String, nullable=False)
    amount = db.Column(db.Integer, default=0)  # amount of the same items
    price = db.Column(db.Numeric(precision=18, scale=2))
    is_ordered = db.Column(db.Boolean, default=False, index=True)

    @declared_attr
    def order_id(cls):
        return db.Column(db.Integer, db.ForeignKey('orders.id'))

    @declared_attr
    def customer_id(cls):
        return db.Column(db.Integer, db.ForeignKey('customers.id'),
                         nullable=False)

    @declared_attr
    def customer(cls):
        return db.relationship('Customer',
                               backref=db.backref('carts', **lazy_cascade))

    @classmethod
    def create(cls, commit=True, **kwargs):
        """ Cart creation method. Accepted params are:
        :param product: BaseProduct or it's subclass instance
        :param product_variant: instance of BaseProductVariant subclass
        :param price_option: instance of BasePriceOption subclass
        :param amount: amount of products to place in cart
        :param customer_id: instance of Customer model
        """
        instance_kwargs = {
            'product_id': str(kwargs['product'].id),
            'product_variant_id': str(kwargs['product_variant'].id),
            'price_option_id': str(kwargs['price_option'].id),
            'customer_id': kwargs['customer'].id,
            'amount': kwargs['amount'],
            'price': kwargs['product'].get_price(kwargs['price_option'].id,
                                                 kwargs['amount']),
        }
        instance = super(CartMixin, cls).create(commit, **instance_kwargs)
        return instance

    @classmethod
    def for_customer(cls, customer, is_ordered=False):
        """ helper method for obtaining cart records for concrete customer
        """
        return cls.query.filter_by(customer_id=customer.id,
                                   is_ordered=is_ordered)

    @classmethod
    def expired(cls, max_age):
        """ Returns all unordered carts with age >= max_age
            :param max_age: timedelta object
        """
        min_created_at = datetime.utcnow() - max_age
        return cls.query.filter(cls.created_at <= min_created_at,
                                cls.is_ordered == False)

    @classmethod
    def get_price(cls, query):
        # return sum(map(attrgetter('price'), carts_query))
        return db.session.query(func.sum(cls.price)) \
            .filter(query._criterion).scalar()

    @classmethod
    def mark_ordered(cls, carts_query, order):
        assert isinstance(order, OrderMixin)
        return carts_query.update({'is_ordered': True, 'order_id': order.id})

    def order_is_paid(self):
        if self.order_id is None:
            return False

        order_cls = get_order_class()
        order = order_cls.query.get(self.order_id)
        return order and order.state == OrderStates.paid