class ShippingPricesTable(BaseTable): record_properties = { 'zone': CountriesZonesEnumerate(mandatory=True, is_indexed=True), 'max-weight': Decimal(is_indexed=True), 'max-quantity': Integer(is_indexed=True), 'price': Decimal(mandatory=True)}
class BaseOrdersProducts(BaseTable): record_properties = { 'name': String(mandatory=True), 'reference': String, 'title': Unicode, 'declination': String, 'quantity': Integer, 'weight': Decimal, 'pre-tax-price': Decimal(mandatory=True), 'tax': Decimal(mandatory=True), }
class Payments_AddPayment(AutoForm): access = 'is_admin' title = MSG(u'Add a new payment') schema = { 'payment_way': PaymentWaysEnumerate(mandatory=True), 'ref': String(mandatory=True), 'user': String, 'state': Boolean, 'amount': Decimal(mandatory=True), 'description': Unicode } widgets = [ SelectWidget('payment_way', title=MSG(u'Payment way')), TextWidget('ref', title=MSG(u'Reference')), TextWidget('user', title=MSG(u'User Id')), BooleanCheckBox('state', title=MSG(u'Payed ?')), TextWidget('amount', title=MSG(u'Amount')), MultilineWidget('description', title=MSG(u'Description')), ] def action(self, resource, context, form): root = context.root if root.get_resource('users/%s' % form['user'], soft=True) is None: context.message = ERROR(u'User do not exist') return shop = get_shop(resource) payments_table = shop.get_resource('payments/%s/payments' % form['payment_way']).handler del form['payment_way'] payments_table.add_record(form) return context.come_back(MSG(u'New payment added !'), goto='./')
class PaymentWayBaseTable(BaseTable): record_properties = { 'ref': String(is_indexed=True, is_stored=True), 'user': String(is_indexed=True), 'state': Boolean(is_indexed=True,), 'amount': Decimal(title=MSG(u'Payment amount')), 'resource_validator': String, 'description': Unicode(title=MSG(u'Payment description'))}
def get_metadata_schema(cls): schema = ShopFolder.get_metadata_schema() schema.update(WorkflowAware.get_metadata_schema()) schema['total_price'] = Decimal(title=MSG(u'Total price')) schema['shipping_price'] = Decimal schema['total_weight'] = Decimal schema['creation_datetime'] = ISODateTime(title=MSG(u'Creation date')) schema['customer_id'] = Users_Enumerate schema['payment_mode'] = PaymentWaysEnumerate schema['shipping'] = ShippingWaysEnumerate schema['delivery_address'] = Integer schema['bill_address'] = Integer # States schema['is_payed'] = Boolean(default=False) schema['is_sent'] = Boolean(default=False) return schema
def test_Decimal(self): for x in [random.uniform(-100,100) for _ in range(10)]: x = decimal.Decimal(str(x)) data = Decimal.encode(x) self.assertEqual(x, Decimal.decode(data))
from dynamic_folder import DynamicFolder from widgets import DeclinationPricesWidget from shop.enumerate_table import EnumerateTable_to_Enumerate from shop.utils import get_shop declination_schema = {#General informations 'reference': String, 'title': Unicode, # Default declination ? 'is_default': Boolean, # Stock 'stock-quantity': Integer(default=0), # Weight 'impact-on-weight': DeclinationImpact, 'weight-impact-value': Decimal(default=decimal(0)), # Price 'impact_on_price': Decimal(default=decimal(0)), 'pro-impact_on_price': Decimal(default=decimal(0)), # Associated images #'associated-image': ImagesEnumerate # XXX Old to delete 'pro-price-impact-value': Decimal(default=decimal(0)), 'impact-on-price': DeclinationImpact, 'price-impact-value': Decimal(default=decimal(0)), } declination_widgets = [ TextWidget('reference', title=MSG(u'Reference')), TextWidget('title', title=MSG(u'Title')), BooleanRadio('is_default', title=MSG(u'Is default ?')),
# Import from standard library from decimal import Decimal as decimal # Import from itools from itools.core import merge_dicts from itools.datatypes import Decimal, Boolean from itools.gettext import MSG # Import from ikaaro from ikaaro.forms import BooleanRadio, TextWidget # Import from shop from shop.payments.payment_way_views import PaymentWay_Configure deposit_schema = { 'percent': Decimal(default=decimal('100.0'), mandatory=True), 'pay_tax': Boolean(mandatory=True) } deposit_widgets = [ TextWidget('percent', title=MSG(u'Deposit amount (in %)')), BooleanRadio('pay_tax', title=MSG(u'Pay deposite with tax ?')) ] class Deposit_Configure(PaymentWay_Configure): title = MSG(u'Configure deposite module') schema = merge_dicts(PaymentWay_Configure.schema, deposit_schema)
from itools.datatypes import Decimal from itools.gettext import MSG # Import from products from shop.products.enumerate import ProductModelsEnumerate from shop.datatypes import UserGroup_Enumerate class DeliveryModes(Enumerate): options = [{ 'name': 'weight', 'value': MSG(u'Depends of weight') }, { 'name': 'quantity', 'value': MSG(u'Depends of quantity') }] delivery_schema = { 'title': Unicode, 'logo': PathDataType, 'description': Unicode, 'enabled': Boolean(default=True), 'mode': DeliveryModes(default='weight'), 'only_this_models': ProductModelsEnumerate(multiple=True), 'only_this_groups': UserGroup_Enumerate(multiple=True), 'insurance': Decimal(default=decimal(0)), 'is_free': Boolean }
############################################# # Product schema ############################################# product_schema = { # General informations 'state': States(mandatory=True, default='public'), 'reference': String, 'product_model': ProductModelsEnumerate, 'title': Unicode(multilingual=True), 'description': Unicode(multilingual=True), 'tags': Tokens, 'subject': Unicode(multilingual=True), 'cover': ImagePathDataType(mandatory=True), # Shippings 'weight': Decimal(default=decimal(0), mandatory=True), 'use_this_shipping_way': ShippingsWaysEnumerate, # Manufacturer / supplier 'manufacturer': ManufacturersEnumerate, 'supplier': SuppliersEnumerate, # XXX Can have more than one supplier ? # Manage stock 'stock-handled': Boolean(mandatory=True), 'stock-quantity': Integer(default=0, mandatory=True), 'stock-option': StockOptions(mandatory=True, default='accept'), 'resupply-quantity': Integer(default=0), 'sold-quantity': Integer(default=0), 'not_buyable_by_groups': Tokens, 'purchase-price': Decimal, # Price 'pre-tax-price': Decimal(default=decimal(0), mandatory=True), 'tax': TaxesEnumerate(mandatory=True),
class Product(WorkflowAware, TagsAware, DynamicFolder): class_id = 'product' class_title = MSG(u'Product') class_description = MSG(u'A product') class_version = '20100812' ################## # Configuration ################## viewbox = Product_ViewBox() viewbox_cls = Product_ViewBox cross_selling_viewbox = Product_CrossSellingViewBox() ################## __fixed_handlers__ = DynamicFolder.__fixed_handlers__ + [ 'images', 'order-photos', 'cross-selling' ] ####################### # Views ####################### view = Product_View() login = Shop_Login() register = Shop_Register() tag_view = viewbox edit = Product_Edit() add_link_file = Product_AddLinkFile() change_product_model = Product_ChangeProductModel() declinations = Product_DeclinationsView() new_declination = Declination_NewInstance() order = GoToSpecificDocument(specific_document='order-photos', title=MSG(u'Manage photos'), access='is_allowed_to_edit') print_product = Product_Print() send_to_friend = Product_SendToFriend() edit_cross_selling = GoToSpecificDocument( specific_document='cross-selling', title=MSG(u'Edit cross selling'), access='is_allowed_to_edit') delete_product = Product_Delete() order_preview = viewbox add_image = CurrentFolder_AddImage() @classmethod def get_metadata_schema(cls): return merge_dicts(DynamicFolder.get_metadata_schema(), WorkflowAware.get_metadata_schema(), product_schema, data=XHTMLBody(multilingual=True)) @staticmethod def _make_resource(cls, folder, name, ctime=None, *args, **kw): from shop.cross_selling import CrossSellingTable if ctime is None: ctime = datetime.now() DynamicFolder._make_resource(cls, folder, name, ctime=ctime, *args, **kw) # Images folder ImagesFolder._make_resource(ImagesFolder, folder, '%s/images' % name, body='', title={'en': u'Images'}) # Order images table PhotoOrderedTable._make_resource(PhotoOrderedTable, folder, '%s/order-photos' % name, title={'en': u'Order photos'}) # Cross Selling table CrossSellingTable._make_resource(CrossSellingTable, folder, '%s/cross-selling' % name, title={'en': u'Cross selling'}) def _get_dynamic_catalog_values(self): values = {} dynamic_schema = self.get_dynamic_schema() for key, datatype in get_product_filters().items(): register_key = 'DFT-%s' % key if key in dynamic_schema: value = self.get_dynamic_property(key, dynamic_schema) if value and getattr(datatype, 'is_range', False): value = int(value * 100) if value: values[register_key] = value # Dynamic indexation register_fields = get_register_fields() model = self.get_product_model() if model is None: return {} for key, datatype in self.get_dynamic_schema().items(): # We index dynamic properties that correspond to # an EnumerateTable datatype. # So we are able to know if enumerate value is used or not if issubclass(datatype, EnumerateTable_to_Enumerate) is True: register_key = 'DFT-%s' % datatype.enumerate_name if register_key not in register_fields: register_field(register_key, String(is_indexed=True)) if datatype.multiple is True: values[register_key] = ' '.join(self.get_property(key)) else: values[register_key] = self.get_property(key) return values def _get_preview_content(self, languages): # TagsAware preview not used for products # Saves half the time of reindexing! return None def _get_catalog_values(self): values = merge_dicts(DynamicFolder._get_catalog_values(self), TagsAware._get_catalog_values(self), self._get_dynamic_catalog_values()) # Data data = self.get_property('data') if data is not None: data = xml_to_text(data) values['data'] = data # Reference values['reference'] = self.get_property('reference') # Manufacturer values['manufacturer'] = str(self.get_property('manufacturer')) # Supplier values['supplier'] = str(self.get_property('supplier')) # Stock quantity values['stock_quantity'] = self.get_property('stock-quantity') # Product models values['product_model'] = str(self.get_property('product_model')) # Images order = self.get_resource('order-photos', soft=True) if order: ordered_names = list(order.get_ordered_names()) values['has_images'] = (len(ordered_names) != 0) else: values['has_images'] = False # Price # XXX We can't sort decimal, so transform to int values['stored_price'] = int(self.get_price_with_tax() * 100) values['stored_weight'] = int(self.get_weight() * 100) # Price values['ht_price'] = self.get_price_without_tax() values['ttc_price'] = self.get_price_with_tax() # Creation time values['ctime'] = self.get_property('ctime') # Publication date values['pub_datetime'] = self.get_property('pub_datetime') # Promotion values['has_reduction'] = self.get_property('has_reduction') # not_buyable_by_groups values['not_buyable_by_groups'] = self.get_property( 'not_buyable_by_groups') return values def get_dynamic_schema(self): product_model = self.get_product_model() if not product_model: return {} return product_model.get_model_schema() def get_product_model(self): product_model = self.get_property('product_model') if not product_model: return None return self.get_resource(product_model) def to_text(self): result = {} site_root = self.get_site_root() languages = site_root.get_property('website_languages') product_model = self.get_product_model() schema = {} if product_model: schema = product_model.get_model_schema() purchase_options_schema = self.get_purchase_options_schema() declinations = list(self.search_resources(cls=Declination)) for language in languages: texts = result.setdefault(language, []) for key in ('title', 'description'): value = self.get_property(key, language=language) if value: texts.append(value) # Parent category current_category = self.parent while current_category.class_id == 'category': texts.append(current_category.get_title(language=language)) current_category = current_category.parent # data (html) events = self.get_property('data', language=language) if events: text = [ unicode(value, 'utf-8') for event, value, line in events if event == TEXT ] if text: texts.append(u' '.join(text)) # Dynamic properties for key, datatype in schema.iteritems(): value = self.get_property(key) if value: text = None multiple = datatype.multiple if issubclass(datatype, Unicode): if multiple: text = ' '.join([x for x in value]) else: text = value elif issubclass(datatype, String): if multiple: text = ' '.join([Unicode.decode(x) for x in value]) else: text = Unicode.decode(value) elif issubclass(datatype, Enumerate): values = value if multiple is False: values = [value] # XXX use multilingual label text = ' '.join(values) if text: texts.append(text) # Manufacturer manufacturer = self.get_property('manufacturer') if manufacturer: manufacturer = site_root.get_resource(manufacturer) texts.append(manufacturer.get_title()) # Purchase options for declination in declinations: for key, datatype in purchase_options_schema.iteritems(): name = declination.get_property(key) value = datatype.to_text(name, languages) if value: texts.append(value) # Join for language, texts in result.iteritems(): result[language] = u'\n'.join(texts) return result def get_resource_icon(self, size=16): context = get_context() size = 48 cover = self.get_property('cover') link = '%s/%s' % (context.get_link(self), cover) return '%s/;thumb?width=%s&height=%s' % (link, size, size) ################################################## ## Purchase options ################################################## def get_purchase_options_names(self): """ Return list of enumerates name corresponding to a purchase option (from the enumerates library) """ model = self.get_product_model() if model is None: return [] return model.get_property('declinations_enumerates') def get_purchase_options_schema(self): schema = {} for name in self.get_purchase_options_names(): schema[name] = EnumerateTable_to_Enumerate(enumerate_name=name) return schema def get_javascript_namespace(self, declinations): # XXX # We have to Add price without tax (Before and after reduction) # XXX If handle_stock property is false manage_stock should be false manage_stock = self.get_stock_option() != 'accept' purchase_options_names = self.get_purchase_options_names() # Base product stock_quantity = self.get_property('stock-quantity') products = {} if len(declinations) == 0: products['base_product'] = { 'price_ht': format_price(self.get_price_without_tax()), 'price_ttc': format_price(self.get_price_with_tax()), 'weight': str(self.get_weight()), 'image': [], 'option': {}, 'is_default': True, 'stock': stock_quantity if manage_stock else None } # Other products (declinations) for declination in declinations: dynamic_schema = declination.get_dynamic_schema() stock_quantity = declination.get_quantity_in_stock() price_ht = self.get_price_without_tax( id_declination=declination.name) price_ttc = self.get_price_with_tax( id_declination=declination.name) image = None #declination.get_property('associated-image') products[declination.name] = { 'price_ht': format_price(price_ht), 'price_ttc': format_price(price_ttc), 'weight': str(declination.get_weight()), 'image': get_uri_name(image) if image else None, 'is_default': declination.get_property('is_default'), 'option': {}, 'stock': stock_quantity if manage_stock else None } for name in purchase_options_names: value = declination.get_dynamic_property(name, dynamic_schema) if value: products[declination.name]['option'][name] = value return dumps(products) def get_purchase_options_namespace(self, declinations): namespace = [] shop = get_shop(self) purchase_options_names = self.get_purchase_options_names() values = {} # Has declination ? if not declinations: return namespace # Get uniques purchase option values for declination in declinations: dynamic_schema = declination.get_dynamic_schema() for name in purchase_options_names: value = declination.get_dynamic_property(name, dynamic_schema) if not value: continue if not values.has_key(name): values[name] = set([]) values[name].add(value) # Build datatype / widget enumerates_folder = shop.get_resource('enumerates') for name in purchase_options_names: if values.get(name) is None or len(values[name]) == 0: continue enumerate_table = enumerates_folder.get_resource(name) datatype = Restricted_EnumerateTable_to_Enumerate( enumerate_name=name, values=values[name]) widget_cls = enumerate_table.widget_cls widget = widget_cls(name, has_empty_option=False) namespace.append({ 'title': enumerate_table.get_title(), 'html': widget.to_html(datatype, None) }) return namespace def get_declination(self, kw): """ Return the name of declination corresponding to a dict of purchase options Example: {'color': 'red', 'size': 'M'} ==> Return name of declination if exist ==> Return None if not exist """ purchase_options_schema = self.get_purchase_options_schema() for declination in self.search_resources(cls=Declination): dynamic_schema = declination.get_dynamic_schema() value = [ kw.get(x) == (declination.get_dynamic_property( x, dynamic_schema) or None) for x in purchase_options_schema ] if set(value) == set([True]): return declination.name return None def get_declination_namespace(self, declination_name): namespace = [] shop = get_shop(self) declination = self.get_resource(declination_name) dynamic_schema = declination.get_dynamic_schema() enumerates_folder = shop.get_resource('enumerates') for name in self.get_purchase_options_names(): value = declination.get_dynamic_property(name, dynamic_schema) if not value: continue enumerate_table = enumerates_folder.get_resource(name) datatype = EnumerateTable_to_Enumerate(enumerate_name=name) namespace.append({ 'title': enumerate_table.get_title(), 'value': datatype.get_value(value) }) return namespace # XXX We should be able to activate it or not #def get_available_languages(self, languages): # from itws.utils import is_empty # available_langs = [] # for language in languages: # events = self.get_property('data', language=language) # title = self.get_property('title', language=language) # if events and is_empty(events) is False and len(title.strip()): # available_langs.append(language) # return available_langs ################################################## ## Namespace ################################################## def get_small_namespace(self, context): title = self.get_property('title') # Specific model is ? specific_model_is = SpecificModelIs() product_model = self.get_product_model() if product_model: product_model_name = product_model.name else: product_model_name = None specific_model_is.model_name = product_model_name # Dynamic property dynamic_property = DynamicProperty() dynamic_property.resource = self dynamic_property.context = context dynamic_property.schema = self.get_dynamic_schema() # Category category = { 'name': self.parent.name, 'href': context.get_link(self.parent), 'title': self.parent.get_title() } # Lang (usefull to show contextuel images) ws_languages = context.site_root.get_property('website_languages') accept = context.accept_language lang = accept.select_language(ws_languages) # Shop modules shop_module = ModuleLoader() shop_module.context = context shop_module.here = self # Minititle mini_title = MiniTitle() mini_title.context = context mini_title.here = self # Return namespace return { 'name': self.name, 'lang': lang, 'module': shop_module, 'specific_model_is': specific_model_is, 'dynamic_property': dynamic_property, 'category': category, 'cover': self.get_cover_namespace(context), 'description': self.get_property('description'), 'href': context.get_link(self), 'manufacturer': ManufacturersEnumerate.get_value( self.get_property('manufacturer')), 'mini-title': mini_title, 'price': self.get_price_namespace(), 'reference': self.get_property('reference'), 'title': title } def get_price_namespace(self): has_reduction = self.get_property('has_reduction') ns = { 'with_tax': self.get_price_with_tax(pretty=True), 'without_tax': self.get_price_without_tax(pretty=True), 'has_reduction': has_reduction } if has_reduction: kw = {'pretty': True, 'with_reduction': False} ns['with_tax_before_reduction'] = self.get_price_with_tax(**kw) ns['without_tax_before_reduction'] = self.get_price_with_tax(**kw) return ns def get_cross_selling_namespace(self, context): from shop.categories import Category table = self.get_resource('cross-selling', soft=True) if table is None: # If table do not exist we use default cross selling shop = get_shop(self) table = shop.get_resource('cross-selling') viewbox = self.cross_selling_viewbox cross_selling = [] abspath = self.get_abspath() parent = self.parent if isinstance(parent, Category): current_category = [parent] elif isinstance(self, Category): current_category = [self] else: current_category = [] cross_products = table.get_products(context, self.class_id, current_category, [abspath]) for product in cross_products: cross_selling.append(viewbox.GET(product, context)) return cross_selling def get_namespace(self, context): root = context.root namespace = { 'name': self.name, 'abspath': self.get_abspath(), 'price': self.get_price_namespace() } # Get basic informations namespace['href'] = context.get_link(self) for key in product_schema.keys(): if key == 'data': continue namespace[key] = self.get_property(key) # Category namespace['category'] = { 'name': self.parent.name, 'href': context.get_link(self.parent), 'breadcrumb_title': self.parent.get_property('breadcrumb_title'), 'title': self.parent.get_title() } # Categorie XXX To remove namespace['categorie'] = self.parent.get_title() # Lang (usefull to show contextuel images) ws_languages = context.site_root.get_property('website_languages') accept = context.accept_language lang = accept.select_language(ws_languages) namespace['lang'] = lang # Manufacturer manufacturer = self.get_property('manufacturer') if manufacturer: manufacturer = root.get_resource(manufacturer) link = context.get_link(manufacturer) photo = manufacturer.get_property('photo') namespace['manufacturer'] = { 'name': manufacturer.name, 'link': link, 'photo': resolve_uri2(link, photo), 'title': manufacturer.get_title() } else: namespace['manufacturer'] = None # Data namespace['data'] = self.get_property('data') # Specific product informations model = self.get_product_model() if model: namespace.update(model.get_model_namespace(self)) else: namespace['specific_dict'] = {} namespace['specific_list'] = [] # Images namespace['cover'] = self.get_cover_namespace(context) namespace['images'] = self.get_images_namespace(context) namespace['has_more_than_one_image'] = len(namespace['images']) > 1 # Product is buyable namespace['is_buyable'] = self.is_buyable(context) # Cross selling namespace['cross_selling'] = self.get_cross_selling_namespace(context) # Authentificated ? ac = self.get_access_control() namespace['is_authenticated'] = ac.is_authenticated(context.user, self) # Configuration configuration = ConfigurationProperty() configuration.context = context configuration.resource = self namespace['configuration'] = configuration # Shop modules shop_module = ModuleLoader() shop_module.context = context shop_module.here = self namespace['module'] = shop_module return namespace ##################### # Images ##################### def get_preview_thumbnail(self): container = self cover = self.get_property('cover') # If no cover get default one if not cover: container = self.parent cover = container.get_property('default_product_cover') # If no cover we return None if not cover: return None return container.get_resource(cover, soft=True) def get_cover_namespace(self, context): image = self.get_preview_thumbnail() if not image: return return { 'href': context.get_link(image), 'name': image.name, 'key': image.handler.key, 'title': image.get_property('title') or self.get_title() } def get_images_namespace(self, context): images = [] for image in self.get_ordered_photos(context): images.append({ 'name': image.name, 'href': context.get_link(image), 'title': image.get_property('title') or self.get_title() }) return images def get_ordered_photos(self, context): # Search photos order = self.get_resource('order-photos', soft=True) # Order table can be remove for performances if order is None: return [] ordered_names = list(order.get_ordered_names()) # If no photos, return if not ordered_names: return [] # Get photos images = [] ac = self.get_access_control() user = context.user for name in ordered_names: image = order.get_resource(name, soft=True) if image and ac.is_allowed_to_view(user, image): images.append(image) return images ##################### ## Stock ##################### def get_stock_option(self): # XXX Get option from shop generatl configuration return self.get_property('stock-option') def is_in_stock_or_ignore_stock(self, quantity, id_declination=None): if self.get_stock_option() == 'accept': return True if id_declination: declination = self.get_resource(id_declination) resource = declination else: resource = self return resource.get_property('stock-quantity') >= quantity def send_alert_stock(self): shop = get_shop(self) context = get_context() root = context.root product_uri = context.uri.resolve('/shop/products/%s/' % self.name) kw = {'product_title': self.get_title(), 'product_uri': product_uri} body = mail_stock_body_template.gettext(**kw) for to_addr in shop.get_property('order_notification_mails'): root.send_email(to_addr=to_addr, subject=mail_stock_subject_template.gettext(), text=body) def remove_from_stock(self, quantity, id_declination=None): stock_option = self.get_stock_option() if stock_option == 'dont_handle': return resource = self if id_declination: declination = self.get_resource(id_declination) resource = declination old_quantity = resource.get_quantity_in_stock() new_quantity = old_quantity - quantity if old_quantity > 0 and new_quantity <= 0: self.send_alert_stock() resource.set_property('stock-quantity', new_quantity) # XXX If is declination go private ? if not id_declination and new_quantity <= 0 and stock_option == 'refuse_go_private': self.set_property('state', 'private') def add_on_stock(self, quantity, id_declination=None): stock_option = self.get_stock_option() if stock_option == 'dont_handle': return resource = self if id_declination: declination = self.get_resource(id_declination) resource = declination old_quantity = resource.get_property('stock-quantity') new_quantity = old_quantity + quantity resource.set_property('stock-quantity', new_quantity) def get_quantity_in_stock(self): return self.get_property('stock-quantity') ##################### ## API ##################### def is_buyable(self, context, quantity=1): shop = get_shop(self) group_name = get_group_name(shop, context) return (self.get_price_without_tax() != decimal(0) and group_name not in self.get_property('not_buyable_by_groups') and self.get_statename() == 'public') def get_reference(self, id_declination=None): if id_declination: declination = self.get_resource(id_declination, soft=True) if declination: reference = declination.get_property('reference') if reference: return reference return self.get_property('reference') def get_price_prefix(self): shop = get_shop(self) context = get_context() group_name = get_group_name(shop, context) if get_uri_name(group_name) == 'pro': return 'pro-' return '' def get_tax_value(self, prefix=None): shop = get_shop(self) if prefix is None: prefix = self.get_price_prefix() # Get zone from cookie id_zone = ProductCart(get_context()).id_zone # If not define... get default zone if id_zone is None: id_zone = shop.get_property('shop_default_zone') # Check if zone has tax ? zones = shop.get_resource('countries-zones').handler zone_record = zones.get_record(int(id_zone)) if zones.get_record_value(zone_record, 'has_tax') is True: tax = self.get_property('%stax' % prefix) tax_value = TaxesEnumerate.get_value(tax) or decimal(0) return (tax_value / decimal(100) + 1) return decimal(1) def get_price_without_tax(self, id_declination=None, with_reduction=True, pretty=False, prefix=None): if prefix is None: prefix = self.get_price_prefix() # Base price if with_reduction is True and self.get_property( '%shas_reduction' % prefix): price = self.get_property('%sreduce-pre-tax-price' % prefix) else: price = self.get_property('%spre-tax-price' % prefix) # Declination if id_declination: declination = self.get_resource(id_declination) price = price + declination.get_price_impact(prefix) # Format price if pretty is True: return format_price(price) return price def get_price_with_tax(self, id_declination=None, with_reduction=True, pretty=False, prefix=None): price = self.get_price_without_tax(id_declination, with_reduction=with_reduction, prefix=prefix) price = price * self.get_tax_value(prefix=prefix) # Format price if pretty is True: return format_price(price) return price def get_weight(self, id_declination=None): if id_declination: declination = self.get_resource(id_declination, soft=True) if declination: return declination.get_weight() return self.get_property('weight') def save_barcode(self, reference): shop = get_shop(self) format = shop.get_property('barcode_format') barcode = generate_barcode(format, reference) if not barcode: return self.del_resource('barcode', soft=True) metadata = { 'title': { 'en': u'Barcode' }, 'filename': 'barcode.png', 'format': 'image/png' } Image.make_resource(Image, self, 'barcode', body=barcode, **metadata) ####################### ## Computed fields ####################### computed_schema = { 'frontoffice_uri': String(title=MSG(u'Frontoffice link')), 'cover_uri': String(title=MSG(u'Cover link')), 'price_with_tax': Decimal(title=MSG(u'Price with tax')) } @property def price_with_tax(self): return self.get_price_with_tax(with_reduction=True) @property def frontoffice_uri(self): shop = get_shop(self) site_root = shop.get_site_root() base_uri = shop.get_property('shop_uri') end_uri = site_root.get_pathto(self) return str(get_reference(base_uri).resolve(end_uri)) @property def cover_uri(self): shop = get_shop(self) site_root = shop.get_site_root() cover = self.get_preview_thumbnail() if not cover: return None base_uri = shop.get_property('shop_uri') end_uri = site_root.get_pathto(cover) return str('%s/;download' % get_reference(base_uri).resolve(end_uri)) ####################### ## Class views ####################### default_class_views = [ 'declinations', 'new_declination', 'images', 'order', 'edit_cross_selling', 'delete_product', 'commit_log' ] @property def class_views(self): context = get_context() # Back-Office hostname = context.uri.authority if hostname[:6] == 'admin.': return ['edit'] + self.default_class_views return ['view', 'edit'] + self.default_class_views def get_links(self): return DynamicFolder.get_links(self) def update_links(self, source, target): return DynamicFolder.update_links(self, source, target) def update_relative_links(self, source): return DynamicFolder.update_relative_links(self, source) def update_20110311(self): # Remove old attributes for key in ['is_buyable', 'reduction', 'categories']: self.del_property(key)
stock = Products_Stock() new_product = Product_NewProduct() def can_paste(self, source): return isinstance(source, Product) def get_document_types(self): return [] # Register fields register_field('reference', String(is_indexed=True, is_stored=True)) register_field('stock_quantity', Integer(is_indexed=True, is_stored=True)) register_field('manufacturer', String(is_indexed=True)) register_field('supplier', String(is_indexed=True, multiple=True)) register_field('product_model', String(is_indexed=True, is_stored=True)) register_field('has_images', Boolean(is_indexed=True, is_stored=True)) register_field('has_reduction', Boolean(is_indexed=True)) register_field('not_buyable_by_groups', String(is_indexed=True, multiple=True)) register_field('ctime', DateTime(is_stored=True, is_indexed=True)) register_field('data', Unicode(is_indexed=True)) register_field('ht_price', Decimal(is_indexed=True, is_stored=True)) register_field('ttc_price', Decimal(is_indexed=True, is_stored=True)) # XXX xapian can't sort decimal register_field('stored_price', Integer(is_indexed=False, is_stored=True)) register_field('stored_weight', Integer(is_indexed=False, is_stored=True)) # Register resources register_resource_class(Product) register_resource_class(Products)
class Decimal_Field(Metadata_Field): datatype = Decimal()
def format_amount(str_value, context): value = Decimal.decode(str_value) value = value / dec('1000') return context.format_number(value, curr=u' k€')