class RoleModel(BasicModel): name = Fields.StringProperty(required=True, verbose_name=u'識別名稱') title = Fields.StringProperty(required=True, verbose_name=u'角色名稱') prohibited_actions = Fields.TextProperty(verbose_name=u'禁止的操作', default='') level = Fields.IntegerProperty(verbose_name=u'權限等級', default=0) @classmethod def create_role(cls, name, title, level, prohibited_actions): n = cls() n.name = name n.title = title n.level = level n.prohibited_actions = prohibited_actions n.put() return n @classmethod def get_role(cls, name): a = cls.query(cls.name == name).get() return a @classmethod def get_list(cls): return cls.query(cls.level < 1000).order(cls.level, -cls.sort) @classmethod def find_lowest_level(cls): return cls.query().order(cls.level).get()
class WarehouseModel(BasicModel): title = Fields.StringProperty(verbose_name=u'倉庫名稱') address = Fields.StringProperty(verbose_name=u'地址') telephone = Fields.StringProperty(verbose_name=u'電話') def stock_in(self, sku, quantity, history, detail_remark): from stock_keeping_unit_in_warehouse_model import StockKeepingUnitInWarehouseModel from ..models.stock_history_detail_model import create_history_detail create_history_detail(history, sku, detail_remark, quantity, self) return StockKeepingUnitInWarehouseModel.stock_in(self, sku, quantity) def stock_out(self, sku, quantity, history, detail_remark, auto_fill=False): from stock_keeping_unit_in_warehouse_model import StockKeepingUnitInWarehouseModel from ..models.stock_history_detail_model import create_history_detail create_history_detail(history, sku, detail_remark, quantity, self) return StockKeepingUnitInWarehouseModel.stock_out( self, sku, quantity, auto_fill) def stock_out_check(self, sku, quantity, auto_fill=False): from stock_keeping_unit_in_warehouse_model import StockKeepingUnitInWarehouseModel return StockKeepingUnitInWarehouseModel.stock_out_check( self, sku, quantity, auto_fill)
class StockHistoryDetailModel(BasicModel): class Meta: label_name = { 'is_enable': u'啟用', 'title': u'完整規格名稱', } history = Fields.KeyProperty(verbose_name=u'歷史記錄', kind=StockHistoryModel) product_name = Fields.StringProperty(verbose_name=u'產品名稱') product_image = Fields.StringProperty(verbose_name=u'產品圖片') sku_object = Fields.KeyProperty(verbose_name=u'SKU 對象') spec_full_name = Fields.SearchingHelperProperty(verbose_name=u'完整規格名稱', target='sku_object', target_field_name='spec_full_name') sku_full_name = Fields.SearchingHelperProperty(verbose_name=u'sku 編號', target='sku_object', target_field_name='sku_full_name') operation_type = Fields.StringProperty(verbose_name=u'操作類型') # 出庫、入庫、轉倉 warehouse = Fields.StringProperty(verbose_name=u'倉庫') warehouse_target = Fields.StringProperty(verbose_name=u'目標倉庫') quantity = Fields.IntegerProperty(verbose_name=u'數量') @classmethod def all_with_history(cls, history): return cls.query(cls.history == history.key).order(-cls.sort) @property def sku_instance(self): if not hasattr(self, '_sku'): self._sku = self.sku_object.get() return self._sku
class PageTrackerModel(BasicModel): name = Fields.StringProperty(verbose_name=u'識別名稱') title = Fields.StringProperty(verbose_name=u'標題') content = Fields.RichTextProperty(verbose_name=u'內容') image = Fields.ImageProperty(verbose_name=u'圖片') is_enable = Fields.BooleanProperty(verbose_name=u'顯示於前台', default=True) @classmethod def all_enable(cls, category=None, *args, **kwargs): return cls.query(cls.is_enable==True).order(-cls.sort) @classmethod def insert(cls, name, title, content=u'', is_enable=True, image=u''): item = cls.get_by_name(name) if item is not None: return if content == u'': content = title item = cls() item.name = name item.title = title item.content = content item.is_enable = is_enable item.image = image item.put() return item
class ConfigModel(BasicConfigModel): title = Fields.HiddenProperty(verbose_name=u'設定名稱', default=u'Line Bot 相關設定') channel_secret = Fields.StringProperty(verbose_name=u'channel_secret', default=u'') channel_access_token = Fields.StringProperty( verbose_name=u'channel_access_token', default=u'') unknown_message = Fields.StringProperty(verbose_name=u'無法判斷時', default=u'') onerror_message = Fields.StringProperty(verbose_name=u'發生錯誤時', default=u'')
class ConfigModel(BasicConfigModel): class Meta: tab_pages = [u'購物時折抵', u'購物時獲得', u'點數購買'] title = Fields.HiddenProperty(verbose_name=u'設定名稱', default=u'購物金設定') max_use_point_for_order = Fields.RangeProperty( verbose_name=u'每筆訂單可折抵最大購物金', unit='%', default=5, step=0.1, multiple=True) discount_ratio = Fields.RangeProperty(verbose_name=u'貨幣折換比率', unit='%', default=100, step=0.1) is_must_use = Fields.BooleanProperty(verbose_name=u'強制使用', default=False) min_amount = Fields.FloatProperty(verbose_name=u'金額閥值', default=0.0) available_point = Fields.RangeProperty(verbose_name=u'每訂單可獲得的購物金', tab_page=1, unit='%', default=0.5, step=0.1) can_buy = Fields.BooleanProperty(verbose_name=u'是否可以直接購買點數', default=False, tab_page=2) give_time = Fields.StringProperty(verbose_name=u'發放時間', default=u'after_order_checkout', choices=[ 'after_order_checkout', 'after_order_close', ], choices_text={ 'after_order_checkout': u'訂單建立後', 'after_order_close': u'訂單完成後', })
class LineBotInputModel(BasicModel): next_step = Fields.StringProperty(verbose_name=u'下一步', default=u'') user_message = Fields.StringProperty(verbose_name=u'訊息暫存', default=u'') need_delete = Fields.BooleanProperty(verbose_name=u'需要刪除', default=False) @classmethod def get_or_create_by_name(cls, name, next_step): item = cls.get_by_name(name) if item is None: item = cls() item.name = name item.next_step = next_step item.put() return item
class ThemesModel(BasicModel): theme_title = Fields.StringProperty(required=True, verbose_name=u'樣式名稱') theme_name = Fields.StringProperty(required=True, verbose_name=u'識別名稱') exclusive = Fields.StringProperty(verbose_name=u'專屬項目', default='all') is_enable = Fields.BooleanProperty(verbose_name=u'顯示於前台', default=True) author = Fields.StringProperty(verbose_name=u'作者') using = Fields.TextProperty(verbose_name=u'使用的欄位') thumbnail = Fields.StringProperty(verbose_name=u'縮圖位置') in_datastore = Fields.BooleanProperty(verbose_name=u'是否位於 DataStore', default=False) @classmethod def find_by_theme_name(cls, theme_name): return cls.query(cls.theme_name == theme_name).get() @classmethod def get_list(cls, identifier_name): return cls.query( cls.exclusive.IN([identifier_name, u'all']) ).order(-cls.sort, -cls._key) @classmethod def check_in_list(cls, identifier_name, theme_name): item = cls.query( ndb.AND( cls.exclusive.IN([identifier_name, u'all']), cls.theme_name == theme_name ) ).order(-cls.sort, -cls._key).get() if item: return True else: return False
class ConfigModel(BasicConfigModel): class Meta: tab_pages = [u'後台設定', u'Manifest 相關設定'] name = Fields.StringProperty(verbose_name=u'識別名稱') style = Fields.StringProperty(default='material', verbose_name=u'後台樣式', choices=('default', 'material')) start_url = Fields.StringProperty(verbose_name=u'manifest start_url', default='/admin#/admin/welcome', tab_page=1) display = Fields.StringProperty(verbose_name=u'manifest display', default='fullscreen', tab_page=1) background_color = Fields.StringProperty( verbose_name=u'manifest background_color', default='#1C5D87', tab_page=1) theme_color = Fields.StringProperty(verbose_name=u'manifest theme_color', default='#1C5D87', tab_page=1) icon_128 = Fields.ImageProperty(verbose_name=u'manifest icon_128', default='', tab_page=1) icon_256 = Fields.ImageProperty(verbose_name=u'manifest icon_256', default='', tab_page=1)
class UserRoleModel(BasicModel): user = Fields.ApplicationUserProperty(verbose_name=u'使用者') role = Fields.KeyProperty(verbose_name=u'角色', kind=RoleModel) @classmethod def get_user_roles(cls, user): return cls.query(cls.user == user.key) @classmethod def get_role(cls, role): if isinstance(role, basestring): role = RoleModel.get_by_name(role) return role @classmethod def set_role(cls, user, role): role = cls.get_role(role) if role is None: return None n = cls.query(cls.user == user.key, cls.role == role.key).get() if n is None: n = cls() n.user = user.key n.role = role.key n.put() return n @classmethod def remove_role(cls, user, role): role = cls.get_role(role) if role is None: return False n = cls.query(cls.user == user.key, cls.role == role.key).get(keys_only=True) if n is not None: ndb.delete_multi([n]) return True @classmethod def has_role(cls, user, role): role = cls.get_role(role) if role is None: return False n = cls.query(cls.user == user.key, cls.role == role.key).get(keys_only=True) if n: return True return False
class UserShopPointProductModel(BasicModel): name = Fields.StringProperty(verbose_name=u'識別名稱') title = Fields.StringProperty(verbose_name=u'標題', default=u'50點') price = Fields.FloatProperty(verbose_name=u'價格', default=50.0) point = Fields.FloatProperty(verbose_name=u'獲得點數', default=50.0) is_enable = Fields.BooleanProperty(verbose_name=u'啟用', default=True) remark = Fields.TextProperty(verbose_name=u'說明', default=u'')
class StockHistoryModel(BasicModel): user = Fields.KeyProperty(verbose_name=u'使用者', kind=ApplicationUserModel) user_name = Fields.StringProperty(verbose_name=u'使用者名稱') status = Fields.BooleanProperty(verbose_name=u'狀態') # True 成功, False 失敗 result = Fields.StringProperty(verbose_name=u'結果訊息') operation = Fields.StringProperty(verbose_name=u'操作類型') # 產品出入庫、訂單出貨、日常調動 remake = Fields.StringProperty(verbose_name=u'摘要') order = Fields.KeyProperty(verbose_name=u'所屬訂單', kind=OrderModel) temporary_key = Fields.StringProperty(verbose_name=u'訂單暫存序號') temporary_items = Fields.SidePanelProperty( verbose_name=u'相關項目', text=u'點擊此處查看 相關項目', auto_open=True, uri='admin:product_stock:stock:side_panel_for_order') @property def details(self): from stock_history_detail_model import StockHistoryDetailModel return StockHistoryDetailModel.all_with_history(self)
class UserShopPointHistoryModel(BasicModel): name = Fields.StringProperty(verbose_name=u'識別名稱') shop_point_target = Fields.KeyProperty(verbose_name=u'所屬單位', kind=UserShopPointModel) order_no = Fields.StringProperty(verbose_name=u'訂單編號', default=u'') order_amount = Fields.StringProperty(verbose_name=u'訂單金額', default=u'') decrease_point = Fields.FloatProperty(verbose_name=u'點數减少', default=0) increase_point = Fields.FloatProperty(verbose_name=u'點數增加', default=0) point = Fields.FloatProperty(verbose_name=u'剩餘點數', default=0) remark = Fields.TextProperty(verbose_name=u'說明', default=u'') @classmethod def all_enable(cls, target=None, *args, **kwargs): if target: return cls.query( cls.shop_point_target == target.key).order(-cls.sort) return cls.query().order(-cls.sort)
class AlbumModel(BasicModel): name = Fields.StringProperty(verbose_name=u'識別名稱') title = Fields.StringProperty(verbose_name=u'相簿名稱') creator = Fields.ApplicationUserProperty(verbose_name=u'建立者') count = Fields.IntegerProperty(verbose_name=u'相片數量', default=0) @property def get_first_img(self): return '/plugins/album/static/empty-album.png' def insert_photo(self, ndb_item): from ..models.album_relation_model import AlbumRelationModel if ndb_item is None: return n = AlbumRelationModel() n.album = self.key n.file = ndb_item.key n.put() self.count += 1 self.put() def all_photo(self): from ..models.album_relation_model import get_all_file_with_album return get_all_file_with_album(self.key)
class CurrencyModel(BasicModel): name = Fields.StringProperty(verbose_name=u'識別名稱') title = Fields.StringProperty(verbose_name=u'幣值名稱') short_name = Fields.StringProperty(verbose_name=u'簡短的名稱', default=u'') unit_name = Fields.StringProperty(verbose_name=u'單位名稱', default=u'元') exchange_rate = Fields.FloatProperty(verbose_name=u'匯率', default=1.0) is_enable = Fields.BooleanProperty(verbose_name=u'顯示於前台', default=True) is_main = Fields.BooleanProperty(verbose_name=u'是否為基準貨幣', default=False) @classmethod def all_enable(cls, category=None, *args, **kwargs): return cls.query(cls.is_enable == True).order(-cls.sort) @classmethod def get_or_create_main_currency(cls): main = cls.get_by_name('main') if main is None: main = cls() main.name = 'main' main.is_main = True main.title = '基準貨幣' main.short_name = u'Main' main.unit_name = u'元' main.is_enable = False main.exchange_rate = 1 main.put() return main @classmethod def get_current_or_main_currency(cls, currency_name): current_currency = cls.get_by_name(currency_name) if current_currency is None: current_currency = CurrencyModel.get_or_create_main_currency() return current_currency @classmethod def get_current_or_main_currency_with_controller(cls, controller): use_currency_name = '' if 'use_currency' in controller.session: use_currency_name = controller.session['use_currency'] if use_currency_name is '': use_currency_name = 'main' return cls.get_current_or_main_currency(use_currency_name) def calc(self, target, places=2): return exchange(self.exchange_rate, target, places)
class MailModel(BasicModel): name = Fields.StringProperty(verbose_name=u'識別名稱') title = Fields.StringProperty(verbose_name=u'樣版名稱') mail_title = Fields.StringProperty(verbose_name=u'信件標題') mail_content = Fields.RichTextProperty(verbose_name=u'信件內容') touch_event = Fields.StringProperty(verbose_name=u'觸發時機', default=u'after_user_signup', choices=[ 'after_user_signup', 'after_order_checkout', 'after_user_verified_email', 'after_user_verified_mobile', 'after_user_verified_both', 'user_request_verified_email', 'user_request_email_reset', 'other', ], choices_text={ 'after_user_signup': u'使用者註冊後', 'after_order_checkout': u'訂單建立後', 'after_user_verified_email': u'使用者驗証信箱後', 'after_user_verified_mobile': u'使用者驗証手機後', 'after_user_verified_both': u'使用者驗証信箱、手機後', 'user_request_verified_email': u'使用者請求驗証信箱', 'user_request_email_reset': u'使用者請求重設密碼', 'other': u'由其它程序處理', }) is_enable = Fields.BooleanProperty(verbose_name=u'啟用觸發', default=True) send_to_admin = Fields.BooleanProperty(verbose_name=u'寄送給管理者', default=False) other_event = Fields.StringProperty(verbose_name=u'其它觸發時機名稱', default=u'') @classmethod def replace_context(cls, name, touch_event=None, title=None, mail_title=None, mail_content=None): record = cls.get_or_create_by_name(name) record.name = name if title: record.title = title if mail_title: record.mail_title = mail_title if mail_content: record.mail_content = mail_content if touch_event: record.touch_event = touch_event record.put()
class LineBotModel(BasicModel): title = Fields.TextProperty(verbose_name=u'檢查的字串', default=u'') py_code = Fields.TextProperty(verbose_name=u'PyCode', default=u'') source_type = Fields.StringProperty(verbose_name=u'要處理的訊息來源', default=u'user', choices=( u'user', u'group', u'room', u'input', u'all', )) message_type = Fields.StringProperty(verbose_name=u'要處理的訊息類型', default=u'text', choices=( u'text', u'image', u'video', u'audio', u'location', u'sticker', u'follow', u'unfollow', u'join', u'leave', u'postback', )) return_message_type = Fields.StringProperty(verbose_name=u'回傳的訊息類型', default=u'TextSendMessage', choices=( u'TextSendMessage', u'ImageSendMessage', u'TemplateSendMessage', )) weights = Fields.FloatProperty(verbose_name=u'權重', default=0.0) @classmethod def get_or_create_by_name(cls, name): item = cls.get_by_name(name) if item is None: import time item = cls() item.name = name item.weights = time.time() item.put() return item
class MailRecordModel(BasicModel): title = Fields.StringProperty(verbose_name=u'標題', default=u'') send_to = Fields.StringProperty(verbose_name=u'發送對象', default=u'') cc = Fields.StringProperty(verbose_name=u'發送對象 cc', default=u'') content = Fields.RichTextProperty(verbose_name=u'信件內容', default=u'') send_date = Fields.DateTimeProperty(verbose_name=u'發送時間', auto_now_add=True) send_system = Fields.HiddenProperty(verbose_name=u'發送的系統', default=u'') @classmethod def replace_context(cls, name, title=None, content=None): record = cls.get_or_create_by_name(name) record.name = name if title: record.title = title if content: record.content = content record.put()
class UserContactDataModel(BasicModel): user = Fields.ApplicationUserProperty(verbose_name=u'使用者', is_lock=True) user_name_proxy = Fields.StringProperty(verbose_name=u'使用者名稱') user_email_proxy = Fields.StringProperty(verbose_name=u'E-Mail') user_avatar_proxy = Fields.ImageProperty(verbose_name=u'頭像') telephone = Fields.StringProperty(verbose_name=u'電話', default=u'') mobile = Fields.StringProperty(verbose_name=u'手機', default=u'') sex = Fields.StringProperty(verbose_name=u'性別', default=u'保密') address_country = Fields.StringProperty(verbose_name=u'國家', default=u'') address_city = Fields.StringProperty(verbose_name=u'縣市', default=u'') address_district = Fields.StringProperty(verbose_name=u'鄉鎮', default=u'') address_detail = Fields.StringProperty(verbose_name=u'地址', default=u'') address_zip = Fields.StringProperty(verbose_name=u'郵遞區號', default=u'') birthday_year = Fields.IntegerProperty(verbose_name=u'出生年', default=0) birthday_month = Fields.IntegerProperty(verbose_name=u'出生月', default=0) birthday_day = Fields.IntegerProperty(verbose_name=u'出生日', default=0) referrals = Fields.StringProperty(verbose_name=u'推薦人', default=u'') is_referrals_change = Fields.BooleanProperty(verbose_name=u'推薦人已修改', default=False) mobile_verification_code = Fields.HiddenProperty(verbose_name=u'手機驗証碼', default=u'') is_mobile_verified = Fields.BooleanProperty(verbose_name=u'手機是否已驗証', default=False) need_verification_mobile = Fields.HiddenProperty(verbose_name=u'需驗証的手機', default=u'') last_verified_mobile = Fields.HiddenProperty(verbose_name=u'最後驗証成功的手機', default=u'') email_verification_code = Fields.HiddenProperty(verbose_name=u'信箱驗証碼', default=u'') is_email_verified_proxy = Fields.BooleanProperty(verbose_name=u'信箱是否已驗証', default=False) need_verification_email = Fields.HiddenProperty(verbose_name=u'需驗証的信箱', default=u'') last_verified_email = Fields.HiddenProperty(verbose_name=u'最後驗証成功的信箱', default=u'') address_verification_code = Fields.HiddenProperty(verbose_name=u'地址驗証碼', default=u'') is_address_verified = Fields.BooleanProperty(verbose_name=u'地址是否已驗証', default=False) @classmethod def get_or_create(cls, user): r = cls.query(cls.user==user.key).get() if r is None: r = cls(user=user.key) r.user_name_proxy = user.name r.user_email_proxy = user.email r.user_avatar_proxy = user.avatar r.is_email_verified_proxy = user.is_email_verified r.put() r._user = user r.password = r._user.password r.old_password = r._user.password return r @property def user_instance(self): if not hasattr(self, '_user'): self._user = self.user.get() return self._user def update_user(self, user=None): if user is None: user = self.user_instance if user is None: return if hasattr(self, 'old_password') and self.old_password != self.password: user.password = self.password user.bycrypt_password() self.password = user.password self.old_password = user.password try: user.name = self.user_name_proxy user.email = self.user_email_proxy user.avatar = self.user_avatar_proxy user.is_email_verified = self.is_email_verified_proxy except: pass user.put() @property def user_name(self): if self.user_instance: return self.user_instance.name return u'該帳號已被刪除' @user_name.setter def user_name(self, value): self.user_name_proxy = value @property def email(self): if self.user_instance: return self.user_instance.email return u'' @email.setter def email(self, value): self.user_email_proxy = value @property def avatar(self): if self.user_instance: return self.user_instance.avatar return u'' @avatar.setter def avatar(self, value): self.user_avatar_proxy = value @property def is_email_verified(self): if self.user_instance: return self.user_instance.is_email_verified return False @is_email_verified.setter def is_email_verified(self, value): self.is_email_verified_proxy = value @classmethod def after_get(cls, key, item): if item.user_instance: item.user_name_proxy = item.user_instance.name item.user_email_proxy = item.user_instance.email item.user_avatar_proxy = item.user_instance.avatar item.is_email_verified_proxy = item.user_instance.is_email_verified else: item.user_name_proxy = u'該帳號已被刪除' def gen_email_verification_code(self, email): import random, string r = ''.join(random.choice(string.lowercase) for i in range(25)) self.need_verification_email = email self.email_verification_code = u'%s-%s-%s-%s' % (r[0:4], r[5:9], r[10:14], r[15:19]) def gen_mobile_verification_code(self, mobile): import random self.need_verification_mobile = mobile self.mobile_verification_code = u'%s' % (random.randint(100000, 999999)) def verify_email(self, code): if self.email_verification_code == code: self.last_verified_email = self.need_verification_email self.is_email_verified_proxy = True self.user_email_proxy = self.last_verified_email self.user_instance.email = self.last_verified_email self.user_instance.is_email_verified = True self.user_instance.put() self.email_verification_code = '' self.put() return True return False def verify_mobile(self, code): if self.mobile_verification_code == code: self.last_verified_mobile = self.need_verification_mobile self.is_mobile_verified = True self.mobile_verification_code = '' self.put() return True return False @property def birthday(self): return '%d-%02d-%02d' % (self.birthday_year, self.birthday_month, self.birthday_day)
class StockKeepingUnitInWarehouseModel(BasicModel): class Meta: label_name = { 'is_enable': u'啟用', 'title': u'完整規格名稱', 'sku_full_name': u'sku 編號' } sku = Fields.KeyProperty(kind=StockKeepingUnitModel, verbose_name=u'所屬 SKU') warehouse = Fields.KeyProperty(kind=WarehouseModel, verbose_name=u'所屬倉庫') product = Fields.KeyProperty(kind=ProductModel, verbose_name=u'所屬產品') quantity = Fields.IntegerProperty(verbose_name=u'在庫數量', default=0) @classmethod def stock_in(cls, warehouse, sku, quantity): record = cls.get_or_create(sku=sku, warehouse=warehouse) record.quantity = int(record.quantity) + int(quantity) record.put() sku.quantity = sku.quantity + quantity sku.last_in_quantity = sku.quantity sku.last_in_datetime = datetime.now() sku.put() return record @classmethod def stock_out_check(cls, warehouse, sku, quantity, auto_fill=False): record = cls.get_or_create(sku=sku, warehouse=warehouse) c = record.quantity - quantity if c < 0 and auto_fill is False: raise ValueError(u'錯誤 [ %s ] 的數量不足 %s 個,缺少 %s 個' % (sku.spec_full_name, str(quantity), str(-c))) @classmethod def stock_out(cls, warehouse, sku, quantity, auto_fill=False): record = cls.get_or_create(sku=sku, warehouse=warehouse) record.quantity = record.quantity - quantity record.put() sku.quantity = sku.quantity - quantity sku.last_out_quantity = sku.quantity sku.last_out_datetime = datetime.now() sku.put() return record @classmethod def get_or_create(cls, sku, warehouse, quantity=0): record = cls.query(cls.sku == sku.key, cls.warehouse == warehouse.key).get() if record is None: record = cls() record.sku = sku.key record.product = sku.product_object record.warehouse = warehouse.key record.quantity = quantity record.put() return record @classmethod def get_all_with_product(cls, product, warehouse=None): if warehouse is None: return cls.query(cls.product == product.key) else: return cls.query(cls.product == product.key, cls.warehouse == warehouse.key) @classmethod def create_sku_by_product(cls, product, warehouse=None): from stock_keeping_unit_model import StockKeepingUnitModel as SKU sku_list = SKU.query(SKU.product_object == product.key).fetch() sku_list_in_warehouse = [] for item in sku_list: sku_list_in_warehouse.append(cls.get_or_create(item, warehouse)) return sku_list_in_warehouse
class UserShopPointModel(BasicModel): class Meta: label_name = { 'modified': u'最後變動時間', } name = Fields.StringProperty(verbose_name=u'識別名稱') user = Fields.ApplicationUserProperty(verbose_name=u'使用者') user_name_proxy = Fields.StringProperty(verbose_name=u'使用者名稱') user_email_proxy = Fields.StringProperty(verbose_name=u'E-Mail') point = Fields.FloatProperty(verbose_name=u'現餘點數', default=0.0) used_point = Fields.FloatProperty(verbose_name=u'已使用點數', default=0.0) @classmethod def get_or_create(cls, user): r = cls.query(cls.user == user.key).get() if r is None: r = cls(user=user.key) r._user = user r.user_name_proxy = r._user.name r.user_email_proxy = r._user.email r.put() r.user_name_proxy = user.name r.user_email_proxy = user.email return r @classmethod def after_get(cls, key, item): super(UserShopPointModel, cls).after_get(key, item) item._user = item.user.get() if item._user: item.user_name_proxy = item._user.name item.user_email_proxy = item._user.email else: item.user_name_proxy = u'該帳號已被刪除' def increase_point(self, point, remark=u'', order_no=u'', order_amount=None): from user_shop_point_history_model import UserShopPointHistoryModel point = int(point) history = UserShopPointHistoryModel() history.shop_point_target = self.key history.remark = remark history.increase_point = point history.order_no = order_no if order_amount is not None: history.order_amount = str(order_amount) if self.point is None: self.point = 0.0 history.point = self.point + point history.put() self.point += point def decrease_point(self, point, remark, order_no=u'', order_amount=None): from user_shop_point_history_model import UserShopPointHistoryModel point = point history = UserShopPointHistoryModel() history.shop_point_target = self.key history.remark = remark history.decrease_point = point history.order_no = order_no if order_amount is not None: history.order_amount = str(order_amount) if self.point is None: self.point = 0.0 history.point = self.point - point history.put() self.point -= point self.used_point += point def get_max_usable_point(self, total): from ..models.config_model import ConfigModel config = ConfigModel.get_config() if total > config.min_amount: n = total * (config.max_use_point_for_order / 100.0) * (config.discount_ratio / 100.0) // 1.0 if n > self.point: return self.point return n else: return 0.0
class FileModel(BasicModel): class Meta: label_name = { 'title': u'顯示名稱', 'display_name': u'顯示名稱', 'children': u'子項目', 'path_as_url': u'URL' } name = Fields.StringProperty(verbose_name=u'名稱') path = Fields.StringProperty(verbose_name=u'檔案路徑') content_length = Fields.IntegerProperty(verbose_name=u'檔案大小', default=0) content_type = Fields.StringProperty(verbose_name=u'檔案類型', default='blob') content_language = Fields.StringProperty(verbose_name=u'語系') parent_resource = Fields.CategoryProperty(kind=selfReferentialModel, verbose_name=u'所屬目錄') is_collection = Fields.BooleanProperty(verbose_name=u'是否為目錄', default=False) is_root = Fields.BooleanProperty(verbose_name=u'是否為根目錄', default=False) created = Fields.DateTimeProperty(auto_now_add=True) modified = Fields.DateTimeProperty(auto_now=True) etag = Fields.StringProperty(verbose_name=u'ETag') resource_data = Fields.CategoryProperty(kind=FileDataModel, verbose_name=u'檔案實體') last_version = Fields.IntegerProperty(verbose_name=u'最新的版本', default=0) last_md5 = Fields.StringProperty(verbose_name=u'MD5', default=u'') file = Fields.BlobKeyProperty(verbose_name=u'BlobKey') theme = Fields.StringProperty(verbose_name=u'所屬樣式', default=u'') @property def abs_path(self): if self.is_collection is False: if str(self.path).startswith('themes/'): return '/assets/' + self.path return '/' + self.path @property def children(self): if self.is_collection: logging.info(self.key) return FileModel().all().filter( FileModel.parent_resource == self.key) else: return [] @property def title(self): return self.name @property def display_name(self): return os.path.basename('%s' % self.path) @property def path_as_url(self): return pathname2url(self.path) @property def content_type_or_default(self): if self.is_collection: return 'httpd/unix-directory' else: mimetype = mimetypes.guess_type(self.path, strict=False)[0] return mimetype if mimetype else 'application/octet-stream' @property def is_code_file(self): return self.content_type in ([ 'css', 'js', 'javascript', 'html', 'text/css', 'text/html', 'text/javascript' ]) @classmethod def all_without_root(cls): return cls.query(cls.is_root == False).order(-cls.sort) @classmethod def code_files(cls): return cls.query( cls.content_type.IN([ 'css', 'js', 'javascript', 'html', 'text/css', 'text/html', 'text/javascript' ]), ).order(-cls.sort, -cls.key) def make_directory(self): path = u'' + self.path if path[0:1] is u'/': path = path[1:] paths = path.split(u'/') last_parent = FileModel.root() if len(paths) > 1: for i in xrange(1, len(paths)): path_str = u'/'.join(paths[:i]) if path_str is not u'': collection = FileModel.get_by_path(path_str) if collection is None: collection = FileModel() collection.name = paths[i] collection.path = path_str collection.parent_resource = last_parent.key collection.is_collection = True collection.put() if collection != self and collection.is_collection is True: last_parent = collection self.parent_resource = last_parent.key self.put() @classmethod def root(cls): root = cls.all().filter(cls.is_root == True).get() if not root: root = cls(path='', is_collection=True) root.name = '-Root-' root.is_root = True root.put() return root @classmethod def all_by_path(cls, path=''): query = cls.all().filter(cls.path == path + '%') return query @classmethod def get_by_path(cls, path=None): return cls.all().filter(cls.path == path).get() if path else cls.root() @classmethod def exists_with_path(cls, path='', is_collection=None): query = cls.all().filter(cls.path == path) if is_collection is not None: query = query.filter(cls.is_collection == True) return query.get(keys_only=True) is not None @classmethod def content_type_sort_by_name(cls, content_type): """ get record with content-type and sort by title """ return cls.query(cls.content_type == content_type).order(cls.name) @classmethod def get_or_create(cls, path, content_type): n = cls.get_by_path(path) if n is None: n = cls() n.name = os.path.basename('%s' % path) n.path = path n.content_type = content_type n.put() return n def move_to_path(self, destination_path): """Moves this resource and all its children (if applicable) to a new path. Assumes that the new path is free and clear.""" if self.is_collection: for child in self.children: child_name = os.path.basename(child.path) child_path = os.path.join(destination_path, child_name) child.move_to_path(child_path) self.path = destination_path self.put() def put(self): # type: () -> object # workaround for general non-solveable issue of no UNIQUE constraint concept in app engine datastore. # anytime we save, we look for the possibility of other duplicate Resources with the same path and delete them. try: for duped_resource in FileModel.all().filter( FileModel.path == self.path): if self.key().id() != duped_resource.key().id(): logging.info( 'Deleting duplicate resource %s with path %s.' % (duped_resource, duped_resource.path)) duped_resource.delete() if self.name != '-Root-': self.name = self.display_name except: pass paths = self.path.split('/') theme = '' if len(paths) >= 2 and paths[0] == 'themes': theme = paths[1] self.theme = theme super(FileModel, self).put() def delete(self): """Override delete to delete our associated ResourceData entity automatically.""" if self.resource_data: n = self.resource_data.get() if n: n.key.delete() if self.last_version > 0: try: from plugins.code.models.code_model import CodeModel CodeModel.delete_with_target(self.key) except: pass self.key.delete() def delete_recursive(self): """Deletes this entity plus all of its children and other descendants.""" if self.is_collection: for child in self.children: child.delete_recursive() self.delete() def export_response(self, href=None): datetime_format = '%Y-%m-%dT%H:%M:%SZ' response = ET.Element('D:response', {'xmlns:D': 'DAV:'}) ET.SubElement(response, 'D:href').text = href propstat = ET.SubElement(response, 'D:propstat') prop = ET.SubElement(propstat, 'D:prop') if self.created: ET.SubElement( prop, 'D:creationdate').text = self.created.strftime(datetime_format) ET.SubElement(prop, 'D:displayname').text = self.display_name if self.content_language: ET.SubElement(prop, 'D:getcontentlanguage').text = str( self.content_language) ET.SubElement(prop, 'D:getcontentlength').text = str(self.content_length) ET.SubElement(prop, 'D:getcontenttype').text = str( self.content_type_or_default) if self.modified: ET.SubElement(prop, 'D:getlastmodified').text = self.modified.strftime( datetime_format) resourcetype = ET.SubElement(prop, 'D:resourcetype') if self.is_collection: ET.SubElement(resourcetype, 'D:collection') ET.SubElement(propstat, 'D:status').text = 'HTTP/1.1 200 OK' return response
class ApplicationUserModel(UserModel): name = Fields.StringProperty(required=True, verbose_name=u'名稱') account = Fields.StringProperty(required=True, verbose_name=u'帳號') password = Fields.StringProperty(required=True, verbose_name=u'密碼') email = Fields.StringProperty(verbose_name=u'E-Mail', default=u'') is_email_verified = Fields.BooleanProperty(verbose_name=u'信箱是否已驗証', default=False) avatar = Fields.ImageProperty(verbose_name=u'頭像') is_enable = Fields.BooleanProperty(verbose_name=u'啟用', default=True) rest_password_token = Fields.StringProperty(verbose_name=u'重設密碼令牌', default=u'') need_check_old_password = Fields.BooleanProperty(verbose_name=u'可設定新密碼', default=True) role = Fields.HiddenProperty(verbose_name=u'角色', default=u'user') provider = Fields.HiddenProperty(verbose_name=u'provider', default=u'website') federated_id = Fields.HiddenProperty(verbose_name=u'federated id', default=u'') @property def title(self): return self.account @classmethod def get_user(cls, account, password, is_enable=True): a = cls.query( cls.account == account, cls.is_enable == is_enable).get() if a is None: return None if bcrypt.hashpw(password, a.password) != a.password: return None return a @classmethod def get_user_by_account(cls, account, check_is_enable=False): if check_is_enable: return cls.query(cls.account == account, cls.is_enable==True).get() return cls.query(cls.account == account).get() @classmethod def get_user_by_email(cls, email, check_is_enable=False): if check_is_enable: return cls.query(cls.email == email, cls.is_enable==True).get() return cls.query(cls.email == email).get() @classmethod def get_user_by_rest_password_token(cls, token, check_is_enable=True): if check_is_enable: return cls.query(cls.rest_password_token == token, cls.is_enable==True).get() return cls.query(cls.rest_password_token == token).get() @classmethod def get_user_by_email_and_password(cls, email, password, check_is_enable=True): a = cls.get_user_by_email(email, check_is_enable) if a is None: return None if bcrypt.hashpw(password, a.password) != a.password: return None return a def check_password(self, check): if bcrypt.hashpw(check, self.password) != self.password: return False return True @classmethod def create_account(cls, name, account, password, avatar=None, email=None): n = cls() n.name = name n.account = account n.password = bcrypt.hashpw(password, bcrypt.gensalt()) n.avatar = avatar if email: n.email = email n.put() return n @classmethod def create_account_by_email(cls, email, password, role=None, avatar=None, account=None, user_name=None): if account is None: account = str(email).split('@')[0] if role is None: role = RoleModel.find_lowest_level() if user_name is None: user_name = account user = cls.create_account(user_name, account, password, '/plugins/backend_ui_material/static/images/users/avatar-001.jpg', email) from ..models.user_role_model import UserRoleModel UserRoleModel.set_role(user, role) return user @classmethod def get_list(cls): return cls.query(cls.account != 'super_user').order(cls.account, -cls.sort, -cls._key) def bycrypt_password_with_old_password(self): if self.old_password != self.new_password: self.password = u'' + bcrypt.hashpw(u'' + self.new_password, bcrypt.gensalt()) self.put() def bycrypt_password(self): self.password = u'' + bcrypt.hashpw(u'' + self.password, bcrypt.gensalt()) self.put() def before_put(self): n = [] try: for r in self.roles: n.append(r.role_name) self.role = ','.join(n) except (BadValueError, AttributeError): self.role = '' @property def roles(self): if not hasattr(self, '_roles'): from user_role_model import UserRoleModel self._roles = UserRoleModel.get_user_roles(self).fetch() return self._roles @property def role_list(self): roles = [] for r in self.roles: roles.append(r.role.get().name) return roles def get_role_level(self, highest=True): if not hasattr(self, '_level'): level = 0 for r in self.roles: r_level = r.role.get().level if r_level > level: level = r_level setattr(self, '_level', level) else: level = getattr(self, '_level') return level def has_role(self, role): from user_role_model import UserRoleModel return UserRoleModel.has_role(self, role) def set_role(self, role): from user_role_model import UserRoleModel return UserRoleModel.set_role(self, role) def remove_role(self, role): from user_role_model import UserRoleModel return UserRoleModel.remove_role(self, role) def check_and_get_role(self, role): from user_role_model import UserRoleModel role = UserRoleModel.get_role(role) if len(self.roles) > 0: for item in self.roles: if item.key == role.key: return role return None else: if UserRoleModel.has_role(self, role): return role return None def has_permission(self, action_full_name, strict=False): if len(self.roles) == 0: return False not_in_count = 0 if not hasattr(self, '_roles_object'): self._roles_object = [] for item in self.roles: r = item.role.get() if r: self._roles_object.append(r) for item in self._roles_object: if action_full_name not in item.prohibited_actions: not_in_count += 1 if strict is False: break if strict: return (not_in_count > 0) and (len(self.roles) == not_in_count) return not_in_count > 0 def gen_password_token(self): from argeweb.core.random_util import gen_random_code self.rest_password_token = gen_random_code() @classmethod def after_delete(cls, key): from user_role_model import UserRoleModel from google.appengine.ext import ndb keys = [] for i in UserRoleModel.query(UserRoleModel.user == key).fetch(): keys.append(i.key) ndb.delete_multi(keys) @classmethod def all_count(cls, *args, **kwargs): """ 回傳目前的總使用者人數 :return: 人數 """ return cls.query().count(keys_only=True) @classmethod def all_count_with_date(cls, date=None, *args, **kwargs): """ 回傳特定日期加入的使用者人數 :return: 人數 """ from datetime import timedelta if date is None: from datetime import datetime date = datetime.today() date_start = date + timedelta(days=-1) date_end = date + timedelta(days=+1) return cls.query(cls.created > date_start, cls.created < date_end).count(keys_only=True)
class ConfigModel(BasicConfigModel): title = Fields.HiddenProperty(verbose_name=u'設定名稱', default=u'購物車設定') use_sku = Fields.BooleanProperty(verbose_name=u'使用產品庫存模組', default=False) use_supplier = Fields.BooleanProperty(verbose_name=u'使用供應商模組', default=True)
class ConfigModel(BasicConfigModel): title = Fields.HiddenProperty(verbose_name=u'設定名稱', default=u'聯絡資料相關設定') use_referrals = Fields.BooleanProperty(verbose_name=u'使用 推薦人 欄位', default=False) use_birthday = Fields.BooleanProperty(verbose_name=u'使用 出生日期 欄位', default=False)
class ShoppingCartItemModel(BasicModel): cart = Fields.KeyProperty(verbose_name=u'購物車', kind=ShoppingCartModel) spec = Fields.KeyProperty(verbose_name=u'產品規格', kind=ProductSpecificationModel) user = Fields.ApplicationUserProperty(verbose_name=u'使用者') product_object = Fields.KeyProperty(verbose_name=u'所屬產品', kind=ProductModel) title = Fields.StringProperty(verbose_name=u'產品名稱', default=u'') product_name = Fields.StringProperty(verbose_name=u'產品名稱(系統)', default=u'') product_no = Fields.StringProperty(verbose_name=u'產品型號', default=u'') product_image = Fields.StringProperty(verbose_name=u'產品圖片', default=u'') price = Fields.FloatProperty(verbose_name=u'銷售價格', default=-1) cost = Fields.FloatProperty(verbose_name=u'成本', default=0.0) spec_full_name = Fields.StringProperty(verbose_name=u'完整規格名稱', default=u'') # 庫存相關 try: from plugins.product_stock.models.stock_keeping_unit_model import StockKeepingUnitModel except ImportError: class StockKeepingUnitModel(BasicModel): pass sku = Fields.KeyProperty(verbose_name=u'最小庫存單位', kind=StockKeepingUnitModel) sku_full_name = Fields.StringProperty(verbose_name=u'產品最小庫存名稱') expired_time = Fields.FloatProperty(verbose_name=u'庫存回收時間') quantity = Fields.IntegerProperty(verbose_name=u'數量', default=0) quantity_has_count = Fields.IntegerProperty(verbose_name=u'已計入庫存的數量', default=0) can_add_to_order = Fields.BooleanProperty(verbose_name=u'加至訂單中', default=False) try: from plugins.supplier.models.supplier_model import SupplierModel except ImportError: class SupplierModel(BasicModel): pass supplier = Fields.CategoryProperty(kind=SupplierModel, verbose_name=u'供應商') # 0=訂購(無庫存), 1=現貨, 2預購 order_type = Fields.StringProperty(verbose_name=u'訂購方式') order_type_value = Fields.IntegerProperty(verbose_name=u'訂購方式(值)') size_1 = Fields.FloatProperty(verbose_name=u'長度(公分)', default=10.0) size_2 = Fields.FloatProperty(verbose_name=u'寬度(公分)', default=10.0) size_3 = Fields.FloatProperty(verbose_name=u'高度(公分)', default=10.0) weight = Fields.FloatProperty(verbose_name=u'重量(公斤)', default=10.0) @classmethod def get_or_create(cls, cart, user, product, spec, order_type=0): try: user_key = user.key except AttributeError: user_key = user item = cls.query(cls.cart == cart.key, cls.user == user_key, cls.product_object == product.key, cls.spec == spec.key, cls.order_type_value == order_type).get() need_put = False if item is None: item = cls() item.cart = cart.key item.user = user_key item.product_object = product.key item.spec = spec.key item.order_type_value = order_type item.order_type = [u'訂購', u'現貨', u'預購'][order_type] item.spec_full_name = spec.full_name item.product_name = product.name item.product_image = product.image need_put = True if order_type == 0: item.can_add_to_order = True for field_name in [ 'title', 'product_no', 'price', 'cost', 'size_1', 'size_2', 'size_3', 'weight' ]: if getattr(item, field_name) != getattr(product, field_name): setattr(item, field_name, getattr(product, field_name)) need_put = True if need_put: item.put() return item @classmethod def get(cls, user, sku, order_type_value=0): return cls.query(cls.sku == sku.key, cls.user == user.key, cls.order_type_value == order_type_value).get() @classmethod def get_or_create_with_spec(cls, user, spec, quantity=0, order_type_value=0): product = spec.product_object.get() item = cls.query(cls.sku == spec.key, cls.user == user.key, cls.order_type_value == order_type_value).get() if item is None: item = cls() item.spec = spec.key item.user = user.key item.order_type_value = order_type_value item.product_object = product.key try: item.supplier = product.supplier.get().key except: pass if order_type_value == 0: item.order_type = u'訂購' else: item.order_type = u'預購' item._spec = spec item._product = product item.title = product.title item.product_no = product.product_no item.product_image = product.image item.spec_full_name = spec.spec_full_name item.change_quantity(quantity) item.put() return item @classmethod def all_with_user(cls, user): key = None if user is not None: key = user.key return cls.query(cls.user == key).order(-cls.sort) @classmethod def before_delete(cls, key): item = key.get() if item.order_type_value == 0: if item.quantity > 0: try: sku = item.sku_instance sku.change_estimate_quantity(item.quantity_has_count) sku.put() except: pass def volumetric_weight(self, divisor=6000.0): n = self.size_1 * self.size_2 * self.size_3 / float(divisor) return n @property def spec_instance(self): if not hasattr(self, '_spec'): self._spec = self.spec.get() return self._spec @property def sku_instance(self): if not hasattr(self, '_sku'): self._sku = self.sku.get() return self._sku @property def product_instance(self): if not hasattr(self, '_product'): self._product = self.spec_instance.product_object.get() return self._product def change_quantity(self, quantity): spec = self.spec_instance product = self.product_instance if self.order_type_value == 0: config = ConfigModel.get_config() if config.stock_recover: self.expired_time = time() + config.stock_recover_time else: self.expired_time = time() + 525600 can_use_quantity = spec.quantity_can_be_used + int( self.quantity_has_count) old_quantity_has_count = self.quantity_has_count if can_use_quantity >= quantity and product.can_order: self.can_add_to_order = True self.quantity = quantity self.quantity_has_count = quantity else: self.can_add_to_order = False self.quantity = 0 self.quantity_has_count = 0 spec.change_estimate_quantity(sub_quantity=old_quantity_has_count, add_quantity=self.quantity) spec.put() else: if product.can_pre_order: self.can_add_to_order = True self.quantity = quantity else: self.can_add_to_order = False self.quantity = 0 spec.change_pre_order_quantity(sub_quantity=int( self.quantity_has_count), add_quantity=self.quantity) spec.put() def quantity_can_be_order(self, user=None, sku=None): if sku is None: sku = self.sku.get() if self.order_type_value > 0: return 999 if user and self.quantity is not None: c = sku.quantity_can_be_used + self.quantity if c > 0: return c return sku.quantity_can_be_used
class ConfigModel(BasicConfigModel): class Meta: tab_pages = [ u'系統收件人', u'Google App Engine', u'MailGun', # u'SendGrid', u'other' ] title = Fields.HiddenProperty(verbose_name=u'設定名稱', default=u'郵件相關設定') use = Fields.IntegerProperty(verbose_name=u'服務類型', default=0) system_recipient_1 = Fields.StringProperty(verbose_name=u'系統收件人 1', tab_page=0, default=u'*****@*****.**') system_recipient_2 = Fields.StringProperty(verbose_name=u'系統收件人 2', tab_page=0, default=u'*****@*****.**') system_recipient_3 = Fields.StringProperty(verbose_name=u'系統收件人 3', tab_page=0, default=u'*****@*****.**') system_recipient_4 = Fields.StringProperty(verbose_name=u'系統收件人 4', tab_page=0, default=u'*****@*****.**') gae_sender_mail = Fields.StringProperty(verbose_name=u'寄件者 E-Mail', tab_page=1, default=u'*****@*****.**') gae_sender_name = Fields.StringProperty(verbose_name=u'寄件者名稱', tab_page=1, default=u'王小華') gae_helper = Fields.HtmlProperty( verbose_name=u'說明', tab_page=1, html= u'Google App Engine 每日可寄送 100 封(收件人),需至 <a href="https://console.cloud.google.com/appengine/settings?project" target="_blank">Google Cloud</a> 設定為 Email API 授權的寄件者。' ) mg_sender_mail = Fields.StringProperty(verbose_name=u'寄件者 E-Mail', tab_page=2, default=u'*****@*****.**') mg_sender_name = Fields.StringProperty(verbose_name=u'寄件者名稱', tab_page=2, default=u'王小華') mg_domain = Fields.StringProperty(verbose_name=u'網域名稱', tab_page=2, default=u'domain.com') mg_api_key = Fields.StringProperty(verbose_name=u'Api Key', tab_page=2, default=u'key-as23XVCBDfg43sfgs') mg_helper = Fields.HtmlProperty( verbose_name=u'說明', tab_page=2, html= u'Mial Gun 每月可免費寄送 10,000 封信件,請至 <a href="https://www.mailgun.com/" target="_blank">Mial Gun</a> 申請相關 Api Key。' )
class ConfigModel(BasicConfigModel): class Meta: tab_pages = [u'串接參數', u'正式環境', u'測試環境'] title = Fields.HiddenProperty(verbose_name=u'設定名稱', default=u'AllPay 相關設定') is_sandbox = Fields.BooleanProperty(verbose_name=u'使用測試伺服器', default=True) return_url = Fields.StringProperty(verbose_name=u'Return Url', tab_page=0, default=u'') client_back_url = Fields.StringProperty(verbose_name=u'Client Back Url', tab_page=0, default=u'') payment_info_url = Fields.StringProperty(verbose_name=u'Payment Info Url', tab_page=0, default=u'') formal_merchant_id = Fields.StringProperty(verbose_name=u'Merchant ID', tab_page=1, default=u'') formal_hash_key = Fields.StringProperty(verbose_name=u'Hash KEY', tab_page=1, default=u'') formal_hash_iv = Fields.StringProperty(verbose_name=u'Hash IV', tab_page=1, default=u'') formal_service_url = Fields.StringProperty( verbose_name=u'正式伺服器網址', tab_page=1, default=u'https://payment.allpay.com.tw/Cashier/AioCheckOut') sandbox_merchant_id = Fields.StringProperty(verbose_name=u'Merchant ID', tab_page=2, default=u'2000132') sandbox_hash_key = Fields.StringProperty(verbose_name=u'Hash KEY', tab_page=2, default=u'5294y06JbISpM5x9') sandbox_hash_iv = Fields.StringProperty(verbose_name=u'Hash IV', tab_page=2, default=u'v77hoKGq4kWxNNIS') sandbox_service_url = Fields.StringProperty( verbose_name=u'測試伺服器網址', tab_page=2, default=u'http://payment-stage.allpay.com.tw/Cashier/AioCheckOut') @property def merchant_id(self): if self.is_sandbox: return self.sandbox_merchant_id return self.formal_merchant_id @property def hash_key(self): if self.is_sandbox: return self.sandbox_hash_key return self.formal_hash_key @property def hash_iv(self): if self.is_sandbox: return self.sandbox_hash_iv return self.formal_hash_iv @property def service_url(self): if self.is_sandbox: return self.sandbox_service_url return self.formal_service_url
class StockKeepingUnitModel(BasicModel): class Meta: label_name = { 'is_enable': u'啟用', 'title': u'完整規格名稱', 'sku_full_name': u'sku 完整編號' } name = Fields.StringProperty(verbose_name=u'識別名稱') product_no = Fields.StringProperty(verbose_name=u'產品編號') sku_no = Fields.StringProperty(verbose_name=u'sku 編號') spec_full_name = Fields.StringProperty(verbose_name=u'完整規格名稱') image = Fields.ImageProperty(verbose_name=u'圖片') use_price = Fields.BooleanProperty(verbose_name=u'使用獨立銷售價格', default=False) price = Fields.FloatProperty(verbose_name=u'獨立銷售價格', default=-1) use_cost = Fields.BooleanProperty(verbose_name=u'使用成本', default=False) cost = Fields.FloatProperty(verbose_name=u'成本', default=0.0) quantity = Fields.IntegerProperty(verbose_name=u'現存數量', default=0) estimate = Fields.IntegerProperty(verbose_name=u'預估數量', default=0) in_order_quantity = Fields.IntegerProperty(verbose_name=u'在訂單中的數量', default=0) pre_order_quantity = Fields.IntegerProperty(verbose_name=u'在預購中的數量', default=0) low_stock_quantity = Fields.IntegerProperty(verbose_name=u'庫存警戒線', default=-1) last_in_quantity = Fields.IntegerProperty(verbose_name=u'最後入庫數量', default=0) last_in_datetime = Fields.DateTimeProperty(verbose_name=u'最後入庫時間', auto_now_add=True) last_out_quantity = Fields.IntegerProperty(verbose_name=u'最後出庫數量', default=0) last_out_datetime = Fields.DateTimeProperty(verbose_name=u'最後入庫時間', auto_now_add=True) is_enable = Fields.BooleanProperty(verbose_name=u'顯示於前台', default=True) can_be_purchased = Fields.BooleanProperty(verbose_name=u'可購買', default=True) use_automatic_increment = Fields.BooleanProperty(verbose_name=u'自動增量', default=False) automatic_increment_quantity = Fields.IntegerProperty( verbose_name=u'增量的數量', default=0) product = Fields.SearchingHelperProperty(verbose_name=u'所屬產品', target='product_object', target_field_name='title') product_object = Fields.KeyProperty(verbose_name=u'所屬產品', kind=ProductModel) spec_name_1 = Fields.HiddenProperty(verbose_name=u'規格名稱 1') spec_name_2 = Fields.HiddenProperty(verbose_name=u'規格名稱 2') spec_name_3 = Fields.HiddenProperty(verbose_name=u'規格名稱 3') spec_name_4 = Fields.HiddenProperty(verbose_name=u'規格名稱 4') spec_name_5 = Fields.HiddenProperty(verbose_name=u'規格名稱 5') spec_value_1 = Fields.HiddenProperty(verbose_name=u'規格值 1') spec_value_2 = Fields.HiddenProperty(verbose_name=u'規格值 2') spec_value_3 = Fields.HiddenProperty(verbose_name=u'規格值 3') spec_value_4 = Fields.HiddenProperty(verbose_name=u'規格值 4') spec_value_5 = Fields.HiddenProperty(verbose_name=u'規格值 5') @property def sku_full_name(self): product_no = u'' if self.product_no is not u'' and self.product_no is not None: product_no = '%s' % self.product_no sku_post_name = u'' if self.name is not u'' and self.name is not None: sku_post_name = self.name if self.sku_no is not u'' and self.sku_no is not None: sku_post_name = self.sku_no if product_no is not u'' and sku_post_name is not u'': return '%s-%s' % (product_no, sku_post_name) return '%s%s' % (product_no, sku_post_name) def before_put(self): super(StockKeepingUnitModel, self).before_put() product_no = u'' cat = self.product_object if cat is not None: try: cat = cat.get() product_no = cat.product_no except: pass spec_list = (u'%s' % self.spec_full_name).split(u',') i = 0 setattr(self, 'product_no', product_no) for index in range(0, len(spec_list)): spec = spec_list[index].split(u':') setattr(self, 'spec_name_%s' % (index + 1), spec[0]) setattr(self, 'spec_value_%s' % (index + 1), spec[1]) @property def title(self): return self.spec_full_name @property def quantity_percent(self): quantity = self.quantity if self.quantity is not None else 0 last_in_quantity = self.last_in_quantity if self.last_in_quantity is not None else 0 if last_in_quantity == 0: return 0 if last_in_quantity < self.low_stock_quantity: last_in_quantity = self.low_stock_quantity return int((float(quantity) / float(last_in_quantity)) * 10000) / 100.0 @property def is_low_stock_level(self): quantity = self.quantity if self.quantity is not None else 0 low_stock_quantity = self.low_stock_quantity if self.low_stock_quantity is not None else -1 return quantity <= low_stock_quantity @classmethod def all_enable(cls, product=None, *args, **kwargs): cat = None if product: cat = ProductModel.get_by_name(product) if cat is None: return cls.query(cls.is_enable == True).order(-cls.sort) else: return cls.query(cls.product_object == cat.key, cls.is_enable == True).order(-cls.sort) @classmethod def find_by_product(cls, product): return cls.query(cls.product_object == product.key).order( cls.spec_full_name) def change_estimate_quantity(self, sub_quantity=0, add_quantity=0): self.estimate = self.estimate - abs(sub_quantity) + abs(add_quantity) if self.estimate < 0: self.estimate = 0 def change_pre_order_quantity(self, sub_quantity=0, add_quantity=0): self.pre_order_quantity = self.pre_order_quantity - abs( sub_quantity) + abs(add_quantity) if self.pre_order_quantity < 0: self.pre_order_quantity = 0 def change_in_order_quantity(self, sub_quantity=0, add_quantity=0): self.in_order_quantity = self.in_order_quantity - abs( sub_quantity) + abs(add_quantity) if self.in_order_quantity < 0: self.in_order_quantity = 0 @property def quantity_can_be_used(self): n = self.quantity - self.in_order_quantity - self.estimate if n < 0: return 0 return n
class AlbumRelationModel(BasicModel): name = Fields.StringProperty(verbose_name=u'識別名稱') file = Fields.KeyProperty(verbose_name=u'檔案', kind=FileModel) album = Fields.KeyProperty(verbose_name=u'相簿', kind=AlbumModel)