def get_price(request, product_slug): """Get base price for a product, returning the answer encoded as JSON.""" quantity = Decimal('1') try: product = Product.objects.get_by_site(active=True, slug=product_slug) except Product.DoesNotExist: return http.HttpResponseNotFound(json_encode(('', _("not available"))), mimetype="text/javascript") prod_slug = product.slug if request.method == "POST" and request.POST.has_key('quantity'): try: quantity = round_decimal(request.POST['quantity'], places=2, roundfactor=.25) except RoundedDecimalError: quantity = Decimal('1.0') log.warn("Could not parse a decimal from '%s', returning '1.0'", request.POST['quantity']) if 'ConfigurableProduct' in product.get_subtypes(): cp = product.configurableproduct chosen_options = optionids_from_post(cp, request.POST) pvp = cp.get_product_from_options(chosen_options) if not pvp: return http.HttpResponse(json_encode(('', _("not available"))), mimetype="text/javascript") prod_slug = pvp.slug price = moneyfmt(pvp.get_qty_price(quantity)) else: price = moneyfmt(product.get_qty_price(quantity)) if not price: return http.HttpResponse(json_encode(('', _("not available"))), mimetype="text/javascript") return http.HttpResponse(json_encode((prod_slug, price)), mimetype="text/javascript")
def __unicode__(self): if self.discount_type == DISCOUNT_AMOUNT: discount = moneyfmt(self.discount) else: discount = u'%s%%' % floatformat(self.discount) return u'%s off %s or more' % (discount, moneyfmt(self.cart_minimum))
def testUSD(self): l10n_settings.set_l10n_setting('default_currency', 'USD') val = Decimal('10.00') self.assertEqual(moneyfmt(val), '$10.00') self.assertEqual(moneyfmt(val, currency_code='USD'), '$10.00')
def set_quantity(request): """Set the quantity for a cart item. Intended to be called via the cart itself, returning to the cart after done. """ cart_url = urlresolvers.reverse('satchmo_cart') if not request.POST: return HttpResponseRedirect(cart_url) success, cart, cartitem, errors = _set_quantity(request) if request.is_ajax(): if errors: return _json_response({'errors': errors, 'results': _("Error")}, True) else: return _json_response({ 'item_id': cartitem.id, 'item_qty': str(cartitem.quantity) or "0", 'item_price': unicode(moneyfmt(cartitem.line_total)) or "0.00", 'cart_total': unicode(moneyfmt(cart.total)) or "0.00", 'cart_count': str(cart.numItems) or '0', }) else: if success: return HttpResponseRedirect(cart_url) else: return display(request, cart = cart, error_message = errors)
def test_checkout(self): """ Run through a full checkout process """ cache_delete() tax = config_get('TAX','MODULE') tax.update('tax.modules.percent') pcnt = config_get('TAX', 'PERCENT') pcnt.update('10') shp = config_get('TAX', 'TAX_SHIPPING') shp.update(False) self.test_cart_adding() response = self.client.post(url('satchmo_checkout-step1'), get_step1_post_data(self.US)) self.assertRedirects(response, url('DUMMY_satchmo_checkout-step2'), status_code=302, target_status_code=200) data = { 'credit_type': 'Visa', 'credit_number': '4485079141095836', 'month_expires': '1', 'year_expires': '2014', 'ccv': '552', 'shipping': 'FlatRate'} response = self.client.post(url('DUMMY_satchmo_checkout-step2'), data) self.assertRedirects(response, url('DUMMY_satchmo_checkout-step3'), status_code=302, target_status_code=200) response = self.client.get(url('DUMMY_satchmo_checkout-step3')) amount = smart_str('Shipping + ' + moneyfmt(Decimal('4.00'))) self.assertContains(response, amount, count=1, status_code=200) amount = smart_str('Tax + ' + moneyfmt(Decimal('4.60'))) self.assertContains(response, amount, count=1, status_code=200) amount = smart_str('Total = ' + moneyfmt(Decimal('54.60'))) self.assertContains(response, amount, count=1, status_code=200) response = self.client.post(url('DUMMY_satchmo_checkout-step3'), {'process' : 'True'}) self.assertRedirects(response, url('DUMMY_satchmo_checkout-success'), status_code=302, target_status_code=200) self.assertEqual(len(mail.outbox), 1) # Log in as a superuser user = User.objects.create_user('fredsu', '*****@*****.**', 'passwd') user.is_staff = True user.is_superuser = True user.save() self.client.login(username='******', password='******') # Test pdf generation order_id = Order.objects.all()[0].id response = self.client.get('/admin/print/invoice/%d/' % order_id) self.assertContains(response, 'reportlab', status_code=200) response = self.client.get('/admin/print/packingslip/%d/' % order_id) self.assertContains(response, 'reportlab', status_code=200) response = self.client.get('/admin/print/shippinglabel/%d/' % order_id) self.assertContains(response, 'reportlab', status_code=200)
def testFake(self): currencies = l10n_settings.get_l10n_setting('currency_formats') currencies['FAKE'] = {'symbol': '^ ', 'positive' : "%(val)0.2f ^", 'negative': "(%(val)0.2f) ^", 'decimal' : ','} l10n_settings.set_l10n_setting('currency_formats', currencies) val = Decimal('10.00') self.assertEqual(moneyfmt(val, currency_code='FAKE'), '10,00 ^') val = Decimal('-50.00') self.assertEqual(moneyfmt(val, currency_code='FAKE'), '(50,00) ^')
def test_checkout(self): """ Run through a full checkout process """ cache_delete() tax = config_get("TAX", "MODULE") tax.update("tax.modules.percent") pcnt = config_get("TAX", "PERCENT") pcnt.update("10") shp = config_get("TAX", "TAX_SHIPPING") shp.update(False) self.test_cart_adding() response = self.client.post(url("satchmo_checkout-step1"), get_step1_post_data(self.US)) self.assertRedirects(response, url("DUMMY_satchmo_checkout-step2"), status_code=302, target_status_code=200) data = { "credit_type": "Visa", "credit_number": "4485079141095836", "month_expires": "1", "year_expires": "2014", "ccv": "552", "shipping": "FlatRate", } response = self.client.post(url("DUMMY_satchmo_checkout-step2"), data) self.assertRedirects(response, url("DUMMY_satchmo_checkout-step3"), status_code=302, target_status_code=200) response = self.client.get(url("DUMMY_satchmo_checkout-step3")) amount = smart_str("Shipping + " + moneyfmt(Decimal("4.00"))) self.assertContains(response, amount, count=1, status_code=200) amount = smart_str("Tax + " + moneyfmt(Decimal("4.60"))) self.assertContains(response, amount, count=1, status_code=200) amount = smart_str("Total = " + moneyfmt(Decimal("54.60"))) self.assertContains(response, amount, count=1, status_code=200) response = self.client.post(url("DUMMY_satchmo_checkout-step3"), {"process": "True"}) self.assertRedirects(response, url("DUMMY_satchmo_checkout-success"), status_code=302, target_status_code=200) self.assertEqual(len(mail.outbox), 1) # Log in as a superuser user = User.objects.create_user("fredsu", "*****@*****.**", "passwd") user.is_staff = True user.is_superuser = True user.save() self.client.login(username="******", password="******") # Test pdf generation order_id = Order.objects.all()[0].id response = self.client.get("/admin/print/invoice/%d/" % order_id) self.assertContains(response, "reportlab", status_code=200) response = self.client.get("/admin/print/packingslip/%d/" % order_id) self.assertContains(response, "reportlab", status_code=200) response = self.client.get("/admin/print/shippinglabel/%d/" % order_id) self.assertContains(response, "reportlab", status_code=200)
def test_custom_product(self): """ Verify that the custom product is working as expected. """ response = self.client.get(prefix+"/") self.assertContains(response, "Computer", count=1) response = self.client.get(prefix+"/product/satchmo-computer/") self.assertContains(response, "Memory", count=1) self.assertContains(response, "Case", count=1) self.assertContains(response, "Monogram", count=1) response = self.client.post(prefix+'/cart/add/', { "productname" : "satchmo-computer", "5" : "1.5gb", "6" : "mid", "custom_monogram": "CBM", "quantity" : '1'}) self.assertRedirects(response, prefix + '/cart/', status_code=302, target_status_code=200) response = self.client.get(prefix+'/cart/') self.assertContains(response, '/satchmo-computer/">satchmo computer', count=1, status_code=200) amount = smart_str(moneyfmt(Decimal('168.00'))) self.assertContains(response, amount, count=4) amount = smart_str('Monogram: CBM ' + moneyfmt(Decimal('10.00'))) self.assertContains(response, amount, count=1) amount = smart_str('Case - External Case: Mid ' + moneyfmt(Decimal('10.00'))) self.assertContains(response, amount, count=1) amount = smart_str('Memory - Internal RAM: 1.5 GB ' + moneyfmt(Decimal('25.00'))) self.assertContains(response, amount, count=1) response = self.client.post(url('satchmo_checkout-step1'), get_step1_post_data(self.US)) self.assertRedirects(response, url('DUMMY_satchmo_checkout-step2'), status_code=302, target_status_code=200) data = { 'credit_type': 'Visa', 'credit_number': '4485079141095836', 'month_expires': '1', 'year_expires': '2012', 'ccv': '552', 'shipping': 'FlatRate'} response = self.client.post(url('DUMMY_satchmo_checkout-step2'), data) self.assertRedirects(response, url('DUMMY_satchmo_checkout-step3'), status_code=302, target_status_code=200) response = self.client.get(url('DUMMY_satchmo_checkout-step3')) amount = smart_str('satchmo computer - ' + moneyfmt(Decimal('168.00'))) self.assertContains(response, amount, count=1, status_code=200) response = self.client.post(url('DUMMY_satchmo_checkout-step3'), {'process' : 'True'}) self.assertRedirects(response, url('DUMMY_satchmo_checkout-success'), status_code=302, target_status_code=200) self.assertEqual(len(mail.outbox), 1)
def apply_discount(sender, instance, **kwargs): """Modifies the specified order's total discount based on the order's total value""" discount = TieredDiscount.objects.valid(instance.sub_total) if discount is not None: log.debug('Found a valid automatic discount: %s' % discount) log.debug('Order discount BEFORE processing: %s' % moneyfmt(instance.discount)) amount = discount.amount(instance.sub_total) instance.discount += amount instance.total -= amount log.debug('Order discount AFTER processing: %s' % moneyfmt(instance.discount)) else: log.debug('No valid automatic discounts found for this order')
def currency(value, args=""): """Convert a value to a money formatted string. places: required number of places after the decimal point curr: optional currency symbol before the sign (may be blank) wrapcents:tag to wrap the part after the decimal point Usage: val|currency val|currency:'places=2' val|currency:'places=2:wrapcents=sup' """ if value == '' or value is None: return value args, kwargs = get_filter_args(args, keywords=('places','curr', 'wrapcents'), intargs=('places',), stripquotes=True) try: value = Decimal(str(value)) except InvalidOperation: log.error("Could not convert value '%s' to decimal", value) raise if not 'places' in kwargs: kwargs['places'] = 2 return mark_safe(moneyfmt(value, **kwargs))
def render(self, context): try: show_tax = self.show_tax.resolve(context) except template.VariableDoesNotExist: show_tax = self.raw_tax if show_tax: tag = CartitemLineTaxedTotalNode(self.raw_cartitem, self.raw_currency) return tag.render(context) try: cartitem = self.cartitem.resolve(context) except template.VariableDoesNotExist: log.warn('Could not resolve template variable: %s', self.cartitem) return '' try: show_currency = self.show_currency.resolve(context) except template.VariableDoesNotExist: show_currency = self.raw_currency if show_currency: return moneyfmt(cartitem.line_total) else: return cartitem.line_total
def balance_due(self): if self.balance: b = self.balance else: b = Decimal('0.00') return moneyfmt(b)
def apply_to_order(self, order): """Apply up to the full amount of the balance of this cert to the order. Returns new balance. """ amount = min(order.balance, self.balance) log.info('applying %s from giftcert #%i [%s] to order #%i [%s]', moneyfmt(amount), self.id, moneyfmt(self.balance), order.id, moneyfmt(order.balance)) processor = get_processor_by_key('PAYMENT_GIFTCERTIFICATE') orderpayment = processor.record_payment(order=order, amount=amount) self.orderpayment = orderpayment return self.use(amount, orderpayment=orderpayment)
def test_product(self): # Test for an easily missed reversion. When you lookup a productvariation product then # you should get the page of the parent configurableproduct, but with the options for # that variation already selected response = self.client.get(prefix+'/product/neat-book-soft/') self.assertContains(response, 'option value="soft" selected="selected"') amount = moneyfmt(Decimal('5.00')) self.assertContains(response, smart_str(amount))
def discount_str(self, instance): """Returns a string representing the discount based on its type""" if instance.discount_type == DISCOUNT_AMOUNT: return moneyfmt(instance.discount) elif instance.discount_type == DISCOUNT_PERCENT: return u'%s%%' % instance.discount else: return instance.discount
def option_total_price(product, option_item): """ Returns the total price as $10.00 The currency symbol is set in the settings.py file """ if option_item.price_change: val = product.unit_price + option_item.price_change else: val = product.unit_price return moneyfmt(val)
def product_shipping(product, args=''): if not args: raise template.TemplateSyntaxError('product_shipping needs the name of the carrier, as product|productshipping:"carrier"') try: c = Carrier.objects.get(key=args) except Carrier.DoesNotExist: raise template.TemplateSyntaxError('product_shipping needs the name of a valid carrier, could not find carrier "%s"' % args) shipping = c.price(product) return mark_safe(moneyfmt(shipping))
def tiered_shipping(price, args=''): if not args: raise template.TemplateSyntaxError('tiered_shipping needs the name of the carrier, as value|tiered_shipping:"carrier"') try: c = Carrier.objects.get(key=args) except Carrier.DoesNotExist: raise template.TemplateSyntaxError('tiered_shipping needs the name of a valid carrier, could not find carrier "%s"' % args) shipping = c.price(Decimal(price)) return mark_safe(moneyfmt(shipping))
def option_total_price(product, option_item): """ Returns the price as (+$1.00) or (-$1.00) depending on the sign of the price change The currency symbol is set in the settings.py file """ if option_item.price_change: val = product.unit_price + option_item.price_change else: val = product.unit_price return moneyfmt(val)
def render(self, context): taxer = _get_taxprocessor(context['request']) try: item = template.resolve_variable(self.cartitem, context) except template.VariableDoesNotExist: raise template.TemplateSyntaxError("No such variable: %s", self.cartitem) total = item.line_total + taxer.by_price(item.product.taxClass, item.line_total) if self.currency: return moneyfmt(total) return total
def amount(self, total): """Returns the discount amount""" amount = Decimal('0.0') if self.discount_type == DISCOUNT_AMOUNT: log.debug('Discounting a fixed amount: %s' % moneyfmt(self.discount)) amount = self.discount elif self.discount_type == DISCOUNT_PERCENT: log.debug('Discounting by a percent of order total: %s' % self.discount) amount = total * self.discount / Decimal('100.0') return amount
def render(self, context): taxer = _get_taxprocessor(context['request']) try: price = template.resolve_variable(self.price, context) except template.VariableDoesNotExist: raise template.TemplateSyntaxError("No such variable: %s", self.price) total = price + taxer.by_price(self.taxclass, price) if self.currency: return moneyfmt(total) return total
def tiered_price_table(carrier): """Creates a table with all shipping tiers""" try: c = Carrier.objects.get(key=carrier) except Carrier.DoesNotExist: raise template.TemplateSyntaxError('tiered_price_table needs the name of a valid carrier, could not find carrier "%s"' % carrier) rows = ['<table class="tiered_price tiered_price_carrier_%s">' % carrier] rows.append('<tr><th>Order Total</th><th>Shipping price</th></tr>') ranges = [] last = None for tier in c.tiers.all().order_by('-price'): t = {} t['price'] = moneyfmt(tier.price) t['from'] = moneyfmt(tier.min_total) if last: t['to'] = moneyfmt(last.min_total) else: t['to'] = None ranges.append(t) last = tier ranges.reverse() for tier in ranges: price = {} price['price'] = tier['price'] if tier['to']: price['range'] = "%s-%s" % (tier['from'], tier['to']) else: price['range'] = "%s+" % (tier['from']) rows.append('<tr><td>%(range)s</td><td>%(price)s</td></tr>' % price) rows.append('</table>') return mark_safe('\n'.join(rows))
def render(self, context): taxer = _get_taxprocessor(context["request"]) try: cart = template.resolve_variable(self.cart, context) except template.VariableDoesNotExist: raise template.TemplateSyntaxError("No such variable: %s", self.cart) total = Decimal("0.00") for item in cart: total += item.line_total + taxer.by_price(item.product.taxClass, item.line_total) if self.currency: return moneyfmt(total) return total
def _activate(self, user): today = datetime.utcnow().date() code = "%x%x%x%x%x" % uuid1().fields[0:5] discount = Discount( site=self.site, description=_(u"Invite discount " u"for %(last_name)s %(first_name)s") % { 'last_name': user.last_name, 'first_name': user.first_name}, code=code, active=True, amount=self.amount, percentage=self.percentage, automatic=False, allowedUses=1, minOrder=self.minOrder, startDate=today, endDate=today + timedelta(days=self.daysValid), shipping=self.shipping, allValid=self.allValid) discount.save() for product in self.valid_products.all(): discount.valid_products.add(product) for category in self.valid_categories.all(): discount.valid_categories.add(category) discount.save() discount_amount = None if self.amount is not None: discount_amount = moneyfmt(self.amount) else: discount_amount = "%s%%" % self.percentage send_store_mail( subject=_(u"A discount coupon for you"), context={ 'first_name': user.first_name, 'invites_number': self.invites_target, 'discount_amount': discount_amount, 'coupon_code': discount.code, 'end_date': discount.endDate}, template='invites/mail/discount.txt', recipients_list=[user.email], template_html='invites/mail/discount.html') # create new InviterDiscount inviter_discount = InviterDiscount( user=user, coupon=discount, template=self) inviter_discount.save() return True
def capture_payment(self, testing=False, order=None, amount=NOTSET): """ Process the transaction and return a ProcessorResponse """ if not order: order = self.order if amount == NOTSET: amount = order.balance payment = None valid_gc = False if self.order.paid_in_full: success = True reason_code = "0" response_text = _("No balance to pay") self.record_payment() else: try: gc = GiftCertificate.objects.from_order(self.order) valid_gc = gc.valid except GiftCertificate.DoesNotExist: success = False reason_code = "1" response_text = _("No such Gift Certificate") if not valid_gc: success = False reason_code = "2" response_text = _("Bad Gift Certificate") else: gc.apply_to_order(self.order) payment = gc.payment reason_code = "0" response_text = _("Success") success = True if not self.order.paid_in_full: response_text = _( "%s balance remains after gift certificate was applied" ) % moneyfmt(self.order.balance) return ProcessorResult(self.key, success, response_text, payment=payment)
def option_price(option_item): """ Returns the price as (+$1.00) or (-$1.00) depending on the sign of the price change The currency symbol is set in the settings.py file """ output = "" if option_item.price_change != 0: amount = moneyfmt(abs(option_item.price_change)) if option_item.price_change < 0: output = "(- %s)" % amount if option_item.price_change > 0: output = "(+ %s)" % amount return output
def _activate(self, user): today = datetime.utcnow().date() code = "%x%x%x%x%x" % uuid1().fields[0:5] discount = Discount( site=self.site, description=_(u"Invite discount " u"for %(last_name)s %(first_name)s") % { 'last_name': user.last_name, 'first_name': user.first_name }, code=code, active=True, amount=self.amount, percentage=self.percentage, automatic=False, allowedUses=1, minOrder=self.minOrder, startDate=today, endDate=today + timedelta(days=self.daysValid), shipping=self.shipping, allValid=self.allValid) discount.save() for product in self.valid_products.all(): discount.valid_products.add(product) for category in self.valid_categories.all(): discount.valid_categories.add(category) discount.save() discount_amount = None if self.amount is not None: discount_amount = moneyfmt(self.amount) else: discount_amount = "%s%%" % self.percentage send_store_mail(subject=_(u"A discount coupon for you"), context={ 'first_name': user.first_name, 'invites_number': self.invites_target, 'discount_amount': discount_amount, 'coupon_code': discount.code, 'end_date': discount.endDate }, template='invites/mail/discount.txt', recipients_list=[user.email], template_html='invites/mail/discount.html') # create new InviterDiscount inviter_discount = InviterDiscount(user=user, coupon=discount, template=self) inviter_discount.save() return True
def set_quantity(request): """Set the quantity for a cart item. Intended to be called via the cart itself, returning to the cart after done. """ cart_url = urlresolvers.reverse('satchmo_cart') if not request.POST: return HttpResponseRedirect(cart_url) success, cart, cartitem, errors = _set_quantity(request) if request.is_ajax(): if errors: return _json_response({ 'errors': errors, 'results': _("Error") }, True) else: return _json_response({ 'item_id': cartitem.id, 'item_qty': str(cartitem.quantity) or "0", 'item_price': unicode(moneyfmt(cartitem.line_total)) or "0.00", 'cart_total': unicode(moneyfmt(cart.total)) or "0.00", 'cart_count': str(cart.numItems) or '0', }) else: if success: return HttpResponseRedirect(cart_url) else: return display(request, cart=cart, error_message=errors)
def product_shipping(product, args=''): if not args: raise template.TemplateSyntaxError( 'product_shipping needs the name of the carrier, as product|productshipping:"carrier"' ) try: c = Carrier.objects.get(key=args) except Carrier.DoesNotExist: raise template.TemplateSyntaxError( 'product_shipping needs the name of a valid carrier, could not find carrier "%s"' % args) shipping = c.price(product) return mark_safe(moneyfmt(shipping))
def tiered_shipping(price, args=''): if not args: raise template.TemplateSyntaxError( 'tiered_shipping needs the name of the carrier, as value|tiered_shipping:"carrier"' ) try: c = Carrier.objects.get(key=args) except Carrier.DoesNotExist: raise template.TemplateSyntaxError( 'tiered_shipping needs the name of a valid carrier, could not find carrier "%s"' % args) shipping = c.price(Decimal(price)) return mark_safe(moneyfmt(shipping))
def render(self, context): taxer = _get_taxprocessor(context['request']) try: cart = template.resolve_variable(self.cart, context) except template.VariableDoesNotExist: raise template.TemplateSyntaxError("No such variable: %s", self.cart) total = Decimal('0.00') for item in cart: total += item.line_total + taxer.by_product_and_price( item.product.taxClass, item.line_total, product=item.product) if self.currency: return moneyfmt(total) return total
def _get_shipping_choices(request, paymentmodule, cart, contact, default_view_tax=False): """Iterate through legal shipping modules, building the list for display to the user. Returns the shipping choices list, along with a dictionary of shipping choices, useful for building javascript that operates on shipping choices. """ shipping_options = [] shipping_dict = {} if not cart.is_shippable: methods = [shipping_method_by_key('NoShipping'),] else: methods = shipping_methods() for method in methods: method.calculate(cart, contact) if method.valid(): template = lookup_template(paymentmodule, 'shipping/options.html') t = loader.get_template(template) shipcost = method.cost() shipping_tax = None taxed_shipping_price = None if config_value_safe('TAX','TAX_SHIPPING', False): shipping_tax = TaxClass.objects.get(title=config_value('TAX', 'TAX_CLASS')) taxer = _get_taxprocessor(request) total = shipcost + taxer.by_price(shipping_tax, shipcost) taxed_shipping_price = moneyfmt(total) c = RequestContext(request, { 'amount': shipcost, 'description' : method.description(), 'method' : method.method(), 'expected_delivery' : method.expectedDelivery(), 'default_view_tax' : default_view_tax, 'shipping_tax': shipping_tax, 'taxed_shipping_price': taxed_shipping_price}) shipping_options.append((method.id, t.render(c))) shipping_dict[method.id] = shipcost return shipping_options, shipping_dict
def do(self): """ Send out daily authorize.net payments to John """ start_of_day_time = datetime.time(0, 0) start_of_today = datetime.datetime.combine(datetime.date.today(), start_of_day_time) today_cutoff_time = start_of_today + datetime.timedelta(hours=15, minutes=45) yesterday_cutoff_time = today_cutoff_time - datetime.timedelta(hours=23, minutes=59, seconds=59) payments = OrderPayment.objects.filter(time_stamp__gte=yesterday_cutoff_time, time_stamp__lt=today_cutoff_time) checkins = OrderCheckin.objects.filter(timestamp__gte=yesterday_cutoff_time, timestamp__lt=today_cutoff_time) todays_total_payments = 0 for payment in payments: todays_total_payments += payment.amount for checkin in checkins: todays_total_payments += checkin.gross_amount() if todays_total_payments > 0: todays_total_payments = moneyfmt(todays_total_payments) send_mail('Sales total for today: %s' % todays_total_payments, 'http://%s%s' % (django_settings.SITE_DOMAIN, reverse('checkin_reports')), django_settings.DEFAULT_FROM_EMAIL, ['*****@*****.**', ] if django_settings.IS_PROD else ['*****@*****.**'], connection=django.core.mail.get_connection(backend='django.core.mail.backends.smtp.EmailBackend') )
def remove(request): """Remove an item from the cart.""" if not request.POST: # Should be a POST request return bad_or_missing(request, "Please use a POST request") success, cart, cartitem, errors = _set_quantity(request, force_delete=True) if request.is_ajax(): if errors: return _json_response({'errors': errors, 'results': _("Error")}, True) else: return _json_response({ 'cart_total': unicode(moneyfmt(cart.total)), 'cart_count': str(cart.numItems), 'item_id': cartitem.id, 'results': success, # Legacy }) else: if errors: return display(request, cart=cart, error_message=errors) else: url = urlresolvers.reverse('satchmo_cart') return HttpResponseRedirect(url)
def remove(request): """Remove an item from the cart.""" if not request.POST: # Should be a POST request return bad_or_missing(request, "Please use a POST request") success, cart, cartitem, errors = _set_quantity(request, force_delete=True) if request.is_ajax(): if errors: return _json_response({'errors': errors, 'results': _("Error")}, True) else: return _json_response({ 'cart_total': six.text_type(moneyfmt(cart.total)), 'cart_count': str(cart.numItems), 'item_id': cartitem.id, 'results': success, # Legacy }) else: if errors: return display(request, cart=cart, error_message=errors) else: url = reverse('satchmo_cart') return HttpResponseRedirect(url)
def render(self, context): try: show_tax = self.show_tax.resolve(context) except template.VariableDoesNotExist: show_tax = self.raw_tax if show_tax: tag = CartTaxedTotalNode(self.raw_cart, self.raw_currency) return tag.render(context) try: cart = self.cart.resolve(context) except template.VariableDoesNotExist: log.warn('Could not resolve template variable: %s', self.cart) return '' try: show_currency = self.show_currency.resolve(context) except template.VariableDoesNotExist: show_currency = self.raw_currency try: show_discount = self.show_discount.resolve(context) except template.VariableDoesNotExist: show_discount = self.raw_show_discount if show_discount: total = cart.total_undiscounted else: total = cart.total if show_currency: return moneyfmt(cart.total) else: return cart.total
def add(request, id=0, redirect_to='satchmo_cart'): """Add an item to the cart.""" log.debug('FORM: %s', request.POST) formdata = request.POST.copy() productslug = None cartplaces = config_value('SHOP', 'CART_PRECISION') roundfactor = config_value('SHOP', 'CART_ROUNDING') if 'productname' in formdata: productslug = formdata['productname'] try: product, details = product_from_post(productslug, formdata) if not (product and product.active): log.debug("product %s is not active" % productslug) return bad_or_missing(request, _("That product is not available at the moment.")) else: log.debug("product %s is active" % productslug) except (Product.DoesNotExist, MultiValueDictKeyError): log.debug("Could not find product: %s", productslug) return bad_or_missing(request, _('The product you have requested does not exist.')) # First we validate that the number isn't too big. if decimal_too_big(formdata['quantity']): return _product_error(request, product, _("Please enter a smaller number.")) # Then we validate that we can round it appropriately. try: quantity = round_decimal(formdata['quantity'], places=cartplaces, roundfactor=roundfactor) except RoundedDecimalError: return _product_error(request, product, _("Invalid quantity.")) if quantity <= Decimal('0'): return _product_error(request, product, _("Please enter a positive number.")) cart = Cart.objects.from_request(request, create=True) # send a signal so that listeners can update product details before we add it to the cart. satchmo_cart_details_query.send( cart, product=product, quantity=quantity, details=details, request=request, form=formdata ) try: added_item = cart.add_item(product, number_added=quantity, details=details) except CartAddProhibited as cap: return _product_error(request, product, cap.message) # got to here with no error, now send a signal so that listeners can also operate on this form. satchmo_cart_add_complete.send(cart, cart=cart, cartitem=added_item, product=product, request=request, form=formdata) satchmo_cart_changed.send(cart, cart=cart, request=request) if request.is_ajax(): data = { 'id': product.id, 'name': product.translated_name(), 'item_id': added_item.id, 'item_qty': str(round_decimal(quantity, 2)), 'item_price': six.text_type(moneyfmt(added_item.line_total)) or "0.00", 'cart_count': str(round_decimal(cart.numItems, 2)), 'cart_total': six.text_type(moneyfmt(cart.total)), # Legacy result, for now 'results': _("Success"), } log.debug('CART AJAX: %s', data) return _json_response(data) else: url = reverse(redirect_to) return HttpResponseRedirect(url)
def productvariation_details(product, include_tax, user, create=False): """Build the product variation details, for conversion to javascript. Returns variation detail dictionary built like so: details = { "OPTION_KEY" : { "SLUG": "Variation Slug", "PRICE" : {"qty" : "$price", [...]}, "SALE" : {"qty" : "$price", [...]}, "TAXED" : "$taxed price", # omitted if no taxed price requested "QTY" : 1 }, [...] } """ ignore_stock = config_value('PRODUCT', 'NO_STOCK_CHECKOUT') discount = find_best_auto_discount(product) use_discount = discount and discount.percentage > 0 if include_tax: from tax.utils import get_tax_processor taxer = get_tax_processor(user=user) tax_class = product.taxClass details = {'SALE': use_discount} variations = ProductPriceLookup.objects.filter( parentid=product.id).order_by("-price") if variations.count() == 0: if create: log.debug('Creating price lookup for %s', product) ProductPriceLookup.objects.smart_create_for_product(product) variations = ProductPriceLookup.objects.filter( parentid=product.id).order_by("-price") else: log.warning( 'You must run satchmo_rebuild_pricing and add it to a cron-job to run every day, or else the product details will not work for product detail pages.' ) for detl in variations: key = detl.key if details.has_key(key): detail = details[key] qty = detl.quantity else: detail = {} detail['SLUG'] = detl.productslug if not detl.active: qty = round_decimal('-1.0') elif ignore_stock: qty = round_decimal('10000.0') else: qty = round_decimal(detl.items_in_stock) detail['QTY'] = round_decimal(qty) detail['PRICE'] = {} if use_discount: detail['SALE'] = {} if include_tax: detail['TAXED'] = {} if use_discount: detail['TAXED_SALE'] = {} if detl.productimage_set: detail['ADDITIONAL_IMAGES'] = [ u"%s" % prodimg.picture for prodimg in detl.productimage_set.all() ] details[key] = detail qtykey = "%d" % detl.quantity price = detl.dynamic_price detail['PRICE'][qtykey] = moneyfmt(price) if use_discount: detail['SALE'][qtykey] = moneyfmt( calc_discounted_by_percentage(price, discount.percentage)) if include_tax: tax_price = taxer.by_price(tax_class, price) + price detail['TAXED'][qtykey] = moneyfmt(tax_price) if use_discount: detail['TAXED_SALE'][qtykey] = moneyfmt( calc_discounted_by_percentage(tax_price, discount.percentage)) return details
def __str__(self): return "%s: %s=%s" % (_('Price Adjustment'), self.label, moneyfmt(self.amount))
def test_custom_product(self): """ Verify that the custom product is working as expected. """ response = self.client.get(prefix + "/") self.assertContains(response, "Computer", count=1) response = self.client.get(prefix + "/product/satchmo-computer/") self.assertContains(response, "Memory", count=1) self.assertContains(response, "Case", count=1) self.assertContains(response, "Monogram", count=1) response = self.client.post( prefix + '/cart/add/', { "productname": "satchmo-computer", "5": "1.5gb", "6": "mid", "custom_monogram": "CBM", "quantity": '1' }) self.assertRedirects(response, prefix + '/cart/', status_code=302, target_status_code=200) response = self.client.get(prefix + '/cart/') self.assertContains(response, '/satchmo-computer/">satchmo computer', count=1, status_code=200) amount = smart_str(moneyfmt(Decimal('168.00'))) self.assertContains(response, amount, count=4) amount = smart_str('Monogram: CBM ' + moneyfmt(Decimal('10.00'))) self.assertContains(response, amount, count=1) amount = smart_str('Case - External Case: Mid ' + moneyfmt(Decimal('10.00'))) self.assertContains(response, amount, count=1) amount = smart_str('Memory - Internal RAM: 1.5 GB ' + moneyfmt(Decimal('25.00'))) self.assertContains(response, amount, count=1) response = self.client.post(reverse('satchmo_checkout-step1'), get_step1_post_data(self.US)) self.assertRedirects(response, reverse('DUMMY_satchmo_checkout-step2'), status_code=302, target_status_code=200) data = { 'credit_type': 'Visa', 'credit_number': '4485079141095836', 'month_expires': '1', 'year_expires': '2020', 'ccv': '552', 'shipping': 'FlatRate' } response = self.client.post(reverse('DUMMY_satchmo_checkout-step2'), data) self.assertRedirects(response, reverse('DUMMY_satchmo_checkout-step3'), status_code=302, target_status_code=200) response = self.client.get(reverse('DUMMY_satchmo_checkout-step3')) amount = smart_str('satchmo computer - ' + moneyfmt(Decimal('168.00'))) self.assertContains(response, amount, count=1, status_code=200) response = self.client.post(reverse('DUMMY_satchmo_checkout-step3'), {'process': 'True'}) self.assertRedirects(response, reverse('DUMMY_satchmo_checkout-success'), status_code=302, target_status_code=200) self.assertEqual(len(mail.outbox), 1)
def _get_shipping_choices(request, paymentmodule, cart, contact, default_view_tax=False, order=None): """Iterate through legal shipping modules, building the list for display to the user. Returns the shipping choices list, along with a dictionary of shipping choices, useful for building javascript that operates on shipping choices. """ shipping_options = [] shipping_dict = {} rendered = {} if not order: try: order = Order.objects.from_request(request) except Order.DoesNotExist: pass discount = None if order: try: discount = Discount.objects.by_code(order.discount_code) except Discount.DoesNotExist: pass if not cart.is_shippable: methods = [ shipping_method_by_key('NoShipping'), ] else: methods = shipping_methods() tax_shipping = config_value_safe('TAX', 'TAX_SHIPPING', False) shipping_tax = None if tax_shipping: taxer = _get_taxprocessor(request) shipping_tax = TaxClass.objects.get( title=config_value('TAX', 'TAX_CLASS')) for method in methods: method.calculate(cart, contact) if method.valid(order=order): template = lookup_template(paymentmodule, 'shipping/options.html') t = loader.get_template(template) shipcost = finalcost = method.cost() if discount and order: order.shipping_cost = shipcost discount.calc(order) shipdiscount = discount.item_discounts.get('Shipping', 0) else: shipdiscount = 0 # set up query to determine shipping price to show shipprice = Price() shipprice.price = shipcost shipadjust = PriceAdjustmentCalc(shipprice) if shipdiscount: shipadjust += PriceAdjustment('discount', _('Discount'), shipdiscount) satchmo_shipping_price_query.send(cart, adjustment=shipadjust) shipdiscount = shipadjust.total_adjustment() if shipdiscount: finalcost -= shipdiscount shipping_dict[method.id] = { 'cost': shipcost, 'discount': shipdiscount, 'final': finalcost } taxed_shipping_price = None if tax_shipping: taxcost = taxer.by_price(shipping_tax, finalcost) total = finalcost + taxcost taxed_shipping_price = moneyfmt(total) shipping_dict[method.id]['taxedcost'] = total shipping_dict[method.id]['tax'] = taxcost c = RequestContext( request, { 'amount': finalcost, 'description': method.description(), 'method': method.method(), 'expected_delivery': method.expectedDelivery(), 'default_view_tax': default_view_tax, 'shipping_tax': shipping_tax, 'taxed_shipping_price': taxed_shipping_price }) rendered[method.id] = t.render(c) #now sort by price, low to high sortme = [(value['cost'], key) for key, value in shipping_dict.items()] sortme.sort() shipping_options = [(key, rendered[key]) for cost, key in sortme] shipping_choices_query.send(sender=cart, cart=cart, paymentmodule=paymentmodule, contact=contact, default_view_tax=default_view_tax, order=order, shipping_options=shipping_options, shipping_dict=shipping_dict) return shipping_options, shipping_dict
def formatted_price(self, obj): """Format the price in the list_display so that the currency symbol shows """ return moneyfmt(obj.unit_price)
def __unicode__(self): sb = moneyfmt(self.start_balance) b = moneyfmt(self.balance) return u"Gift Cert: %s/%s" % (sb, b)
def __str__(self): sb = moneyfmt(self.start_balance) b = moneyfmt(self.balance) return "Gift Cert: %s/%s" % (sb, b)
def cart_min_currency(self, instance): return moneyfmt(instance.cart_minimum)
added_item = cart.add_item(product, number_added=quantity, details=details) except CartAddProhibited, cap: return _product_error(request, product, cap.message) # got to here with no error, now send a signal so that listeners can also operate on this form. satchmo_cart_add_complete.send(cart, cart=cart, cartitem=added_item, product=product, request=request, form=formdata) satchmo_cart_changed.send(cart, cart=cart, request=request) if request.is_ajax(): data = { 'id': product.id, 'name': product.translated_name(), 'item_id': added_item.id, 'item_qty': str(round_decimal(quantity, 2)), 'item_price': unicode(moneyfmt(added_item.line_total)) or "0.00", 'cart_count': str(round_decimal(cart.numItems, 2)), 'cart_total': unicode(moneyfmt(cart.total)), # Legacy result, for now 'results': _("Success"), } log.debug('CART AJAX: %s', data) return _json_response(data) else: url = urlresolvers.reverse(redirect_to) return HttpResponseRedirect(url) def add_ajax(request, id=0, **kwargs): # Allow for legacy apps to still use this url if not request.META.has_key('HTTP_X_REQUESTED_WITH'):
def test_checkout(self): """ Run through a full checkout process """ cache_delete() tax = config_get('TAX', 'MODULE') tax.update('tax.modules.percent') pcnt = config_get('TAX', 'PERCENT') pcnt.update('10') shp = config_get('TAX', 'TAX_SHIPPING_PERCENT') shp.update(False) self.test_cart_adding() response = self.client.post(reverse('satchmo_checkout-step1'), get_step1_post_data(self.US)) self.assertRedirects(response, reverse('DUMMY_satchmo_checkout-step2'), status_code=302, target_status_code=200) data = { 'credit_type': 'Visa', 'credit_number': '4485079141095836', 'month_expires': '1', 'year_expires': '2020', 'ccv': '552', 'shipping': 'FlatRate' } response = self.client.post(reverse('DUMMY_satchmo_checkout-step2'), data) self.assertRedirects(response, reverse('DUMMY_satchmo_checkout-step3'), status_code=302, target_status_code=200) response = self.client.get(reverse('DUMMY_satchmo_checkout-step3')) amount = smart_str('Shipping + ' + moneyfmt(Decimal('4.00'))) self.assertContains(response, amount, count=1, status_code=200) amount = smart_str('Tax + ' + moneyfmt(Decimal('4.60'))) self.assertContains(response, amount, count=1, status_code=200) amount = smart_str('Total = ' + moneyfmt(Decimal('54.60'))) self.assertContains(response, amount, count=1, status_code=200) response = self.client.post(reverse('DUMMY_satchmo_checkout-step3'), {'process': 'True'}) self.assertRedirects(response, reverse('DUMMY_satchmo_checkout-success'), status_code=302, target_status_code=200) self.assertEqual(len(mail.outbox), 1) # Log in as a superuser user = User.objects.create_user('fredsu', '*****@*****.**', 'passwd') user.is_staff = True user.is_superuser = True user.save() self.client.login(username='******', password='******') # Test invoice, packing slip and shipping label generation order_id = Order.objects.all()[0].id response = self.client.get('/admin/print/invoice/%d/' % order_id) self.assertEqual(response.status_code, 200) response = self.client.get('/admin/print/packingslip/%d/' % order_id) self.assertEqual(response.status_code, 200) response = self.client.get('/admin/print/shippinglabel/%d/' % order_id) self.assertEqual(response.status_code, 200)
def capture_payment(self, request, testing=False, order=None, amount=None): """ Process the transaction and return a ProcessorResponse """ if not order: order = self.order if amount is None: amount = order.balance payment = None points = int(request.POST.get('points', None)) if self.order.paid_in_full: success = True reason_code = "0" response_text = _("No balance to pay") self.record_payment() else: #contact = Contact.objects.from_request(request) if points: reward, created = Reward.objects.get_or_create( contact=order.contact) if points > reward.points: success = False reason_code = "0" response_text = _( "You Do not have that many reward points.") return ProcessorResult(self.key, success, response_text, payment=payment) point_modifier = config_value('PAYMENT_REWARD', 'POINTS_VALUE') point_value = point_modifier * points if point_value > order.balance: success = False reason_code = "0" response_text = _( "You are trying to use too many points for this order." ) return ProcessorResult(self.key, success, response_text, payment=payment) else: points_used = points point_value_used = point_value orderpayment = self.record_payment(order=order, amount=point_value_used) reward_item = RewardItem.objects.add_order_payment( reward, order, orderpayment, points_used, point_value_used) reason_code = "0" response_text = _("Success") success = True if not self.order.paid_in_full: url = reverse('satchmo_balance_remaining') response_text = _("%s balance remains after using points" ) % moneyfmt(self.order.balance) else: success = False reason_code = "0" response_text = _( "Please enter the amount of reward points you want to redeem" ) return ProcessorResult(self.key, success, response_text, payment=payment)
def test_two_checkouts_dont_duplicate_contact(self): """ Two checkouts with the same email address do not duplicate contacts Ticket #1264 [Invalid] """ tax = config_get('TAX', 'MODULE') tax.update('tax.modules.percent') pcnt = config_get('TAX', 'PERCENT') pcnt.update('10') shp = config_get('TAX', 'TAX_SHIPPING_PERCENT') shp.update(False) # First checkout self.test_cart_adding() response = self.client.post(reverse('satchmo_checkout-step1'), get_step1_post_data(self.US)) self.assertRedirects(response, reverse('DUMMY_satchmo_checkout-step2'), status_code=302, target_status_code=200) data = { 'credit_type': 'Visa', 'credit_number': '4485079141095836', 'month_expires': '1', 'year_expires': '2020', 'ccv': '552', 'shipping': 'FlatRate' } response = self.client.post(reverse('DUMMY_satchmo_checkout-step2'), data) self.assertRedirects(response, reverse('DUMMY_satchmo_checkout-step3'), status_code=302, target_status_code=200) response = self.client.get(reverse('DUMMY_satchmo_checkout-step3')) amount = smart_str('Shipping + ' + moneyfmt(Decimal('4.00'))) self.assertContains(response, amount, count=1, status_code=200) amount = smart_str('Tax + ' + moneyfmt(Decimal('4.60'))) self.assertContains(response, amount, count=1, status_code=200) amount = smart_str('Total = ' + moneyfmt(Decimal('54.60'))) self.assertContains(response, amount, count=1, status_code=200) response = self.client.post(reverse('DUMMY_satchmo_checkout-step3'), {'process': 'True'}) self.assertRedirects(response, reverse('DUMMY_satchmo_checkout-success'), status_code=302, target_status_code=200) # Second checkout self.test_cart_adding() response = self.client.post(reverse('satchmo_checkout-step1'), get_step1_post_data(self.US)) self.assertRedirects(response, reverse('DUMMY_satchmo_checkout-step2'), status_code=302, target_status_code=200) data = { 'credit_type': 'Visa', 'credit_number': '4485079141095836', 'month_expires': '1', 'year_expires': '2020', 'ccv': '552', 'shipping': 'FlatRate' } response = self.client.post(reverse('DUMMY_satchmo_checkout-step2'), data) self.assertRedirects(response, reverse('DUMMY_satchmo_checkout-step3'), status_code=302, target_status_code=200) response = self.client.get(reverse('DUMMY_satchmo_checkout-step3')) amount = smart_str('Shipping + ' + moneyfmt(Decimal('4.00'))) self.assertContains(response, amount, count=1, status_code=200) amount = smart_str('Tax + ' + moneyfmt(Decimal('4.60'))) self.assertContains(response, amount, count=1, status_code=200) amount = smart_str('Total = ' + moneyfmt(Decimal('54.60'))) self.assertContains(response, amount, count=1, status_code=200) response = self.client.post(reverse('DUMMY_satchmo_checkout-step3'), {'process': 'True'}) self.assertRedirects(response, reverse('DUMMY_satchmo_checkout-success'), status_code=302, target_status_code=200) self.assertEqual(len(mail.outbox), 2) qs = Contact.objects.filter(email="*****@*****.**") # We expects that the contact do not duplicate self.assertEqual(len(qs), 1)
def __unicode__(self): return u"%s: %s=%s" % (_('Price Adjustment'), self.label, moneyfmt(self.amount))
def order_link(self): return mark_safe(u'<a href="/admin/shop/order/%i/">%s #%i (%s)</a>' % (self.order.id, ugettext('Order'), self.order.id, moneyfmt(self.order.total)))