def remove_from_order(item_id): service_user = users.get_current_user() customer = get_customer(service_user) azzert(customer) customer_store_order_key = Order.create_key( customer.id, Order.CUSTOMER_STORE_ORDER_NUMBER) order_item_key = OrderItem.create_key(customer.id, Order.CUSTOMER_STORE_ORDER_NUMBER, item_id) order_item, order = db.get([order_item_key, customer_store_order_key]) if not order_item: logging.warn( "Customer %s tried to delete an already deleted item (%s)", service_user.email(), item_id) return RETURNSTATUS_TO_SUCCESS azzert(order) # Subtract the price from this product, then remove the product. vat = order_item.count * order_item.price * order.vat_pct / 100 total = order_item.count * order_item.price total_inc_vat = total + vat order.amount -= total order.total_amount -= total_inc_vat order.vat -= vat order_item.delete() order.put() return RETURNSTATUS_TO_SUCCESS
def trans(customer_key): customer, order = db.get( [customer_key, Order.create_key(customer_key.id(), order_number)]) if order.manager: customer.manager = order.manager customer.put()
def get_order_items(): # get the order items for this customer from the latest order that isn't signed yet service_user = users.get_current_user() customer = get_customer(service_user) if not customer: return [] order_key = Order.create_key(customer.id, Order.CUSTOMER_STORE_ORDER_NUMBER) order = Order.get(order_key) if order: sln_settings = get_solution_settings(service_user) lang = sln_settings.main_language remaining_length, sub_order = get_subscription_order_remaining_length( customer.id, customer.subscription_order_number) subscription_order_charge_date = format_date( datetime.datetime.utcfromtimestamp(sub_order.next_charge_date), locale=lang) order_items = list(OrderItem.list_by_order(order_key)) order_items_updated = list() to_put = list() to_get = list( set([Product.create_key(o.product_code) for o in order_items] + [Product.create_key(Product.PRODUCT_EXTRA_CITY)])) products = {p.code: p for p in db.get(to_get)} # update the order items if necessary. for order_item in order_items: if products[ order_item. product_code].is_subscription_extension and order_item.count != remaining_length: order_item.count = remaining_length to_put.append(order_item) order_items_updated.append(order_item) if to_put: db.put(to_put) extra_city_price = format_price( products[Product.PRODUCT_EXTRA_CITY].price, sln_settings.currency) service_visible_in_translation = translate( lang, SOLUTION_COMMON, 'service_visible_in_app', subscription_expiration_date=subscription_order_charge_date, amount_of_months=remaining_length, extra_city_price=extra_city_price, app_name='%(app_name)s') return [ OrderItemTO.create(i, service_visible_in_translation) for i in order_items_updated ] else: return []
def cancel_charge(customer_id, order_number, charge_id): """Cancels a charge so adjustments can be made to the order. Rolls back the next charge date of the subscription order. Args: customer_id: order_number: charge_id: Returns: None """ to_put = list() now_ = now() charge, order, customer = db.get([Charge.create_key(charge_id, order_number, customer_id), Order.create_key(customer_id, order_number), Customer.create_key(customer_id)]) charge.date_cancelled = now_ charge.status = Charge.STATUS_CANCELLED to_put.append(charge) order_items = list(OrderItem.list_by_order(order.key())) if order.is_subscription_order: months = 0 for item in order_items: product = item.product if product.is_subscription and product.price > 0: months += item.count if not product.is_subscription and product.extra_subscription_months > 0: months += product.extra_subscription_months if months > 0: next_charge_datetime = datetime.datetime.utcfromtimestamp(now()) - relativedelta(months=months) order.next_charge_date = get_epoch_from_datetime(next_charge_datetime) else: order.next_charge_date = Order.default_next_charge_date() else: extra_months = 0 for item in order_items: product = item.product if not product.is_subscription and product.extra_subscription_months > 0: extra_months += product.extra_subscription_months if extra_months > 0: sub_order = Order.get_by_order_number(customer_id, customer.subscription_order_number) next_charge_datetime = datetime.datetime.utcfromtimestamp(sub_order.next_charge_date) - relativedelta( months=extra_months) sub_order.next_charge_date = get_epoch_from_datetime(next_charge_datetime) to_put.append(sub_order) db.put(to_put)
def fromCustomerModel(customer, can_edit, is_admin): c = CustomerTO() c.name = customer.name c.address1 = customer.address1 c.address2 = customer.address2 c.zip_code = customer.zip_code c.city = customer.city c.country = customer.country c.vat = customer.vat c.organization_type = customer.organization_type c.id = customer.id c.service_email = customer.service_email c.user_email = customer.user_email c.auto_login_url = customer.auto_login_url if customer.service_email else None c.stripe_valid = customer.stripe_valid c.migration_job = customer.migration_job c.manager = customer.manager.email().decode( 'utf-8') if customer.manager else None c.app_ids = customer.sorted_app_ids c.extra_apps_count = customer.extra_apps_count c.prospect_id = customer.prospect_id c.language = customer.language c.subscription_type = customer.SUBSCRIPTION_TYPES[ customer.subscription_type] c.has_loyalty = customer.has_loyalty c.creation_time = customer.creation_time c.team_id = customer.team_id c.can_edit = can_edit c.is_admin = is_admin c.service_disabled_at = customer.service_disabled_at if customer.subscription_cancel_pending_date != 0 and customer.subscription_order_number: c.cancelling_on_date = Order.get( Order.create_key( customer.id, customer.subscription_order_number)).next_charge_date else: c.cancelling_on_date = 0 c.service_disabled_reason = customer.disabled_reason or customer.disabled_reason_str c.service_disabled_reason_int = customer.disabled_reason_int c.subscription_cancel_pending_date = customer.subscription_cancel_pending_date c.website = customer.website c.facebook_page = customer.facebook_page return c
def test_recurrent_billing(self): self.set_datastore_hr_probability(1) products_to_order = [(u'MSUP', 12), (Product.PRODUCT_BEACON, 1)] subscription_order, customer = self._create_customer_and_subscription_order(products_to_order) self._create_service(customer) # Turn back next_charge_date more than 12 months subscription_order.next_charge_date -= 367 * 86400 subscription_order.put() # execute recurrent billing code _create_charge(subscription_order.key(), now(), Product.get_products_dict()) # check if a ShopTask was created task = ShopTask.all().get() self.assertEqual(task.type, ShopTask.TYPE_SUPPORT_NEEDED) task.delete() # Check if an expiredsubscription was created expired_subscription = ExpiredSubscription.get_by_customer_id(customer.id) self.assertIsNotNone(expired_subscription) # first set the expired subscription as 'customer will link his credit card' set_expired_subscription_status(customer.id, ExpiredSubscription.STATUS_WILL_LINK_CREDIT_CARD) task = ShopTask.all().get() self.assertIsNotNone(task) self.assertEqual(task.type, ShopTask.TYPE_CHECK_CREDIT_CARD) task.delete() # extend the customer's (expired) subscription charge = set_expired_subscription_status(customer.id, ExpiredSubscription.STATUS_EXTEND_SUBSCRIPTION) subscription_order, \ expired_subscription = db.get((Order.create_key(customer.id, customer.subscription_order_number), ExpiredSubscription.create_key(customer.id))) self.assertEqual(expired_subscription, None) product_subscription = Product.get_by_code(u'MSUP') charge_total = product_subscription.price * 12 # subscription extensions should always be 12 months long self.assertIsNotNone(charge) self.assertEqual(Charge.TYPE_SUBSCRIPTION_EXTENSION, charge.type) self.assertEqual(charge_total, charge.amount) one_year_from_now_plus_one_day = datetime.datetime.utcfromtimestamp(now()) + relativedelta.relativedelta( months=12, days=1) one_year_from_now_minus_one_day = datetime.datetime.utcfromtimestamp(now()) + relativedelta.relativedelta( months=12, days=-1) self.assertLess(subscription_order.next_charge_date, int(one_year_from_now_plus_one_day.strftime('%s'))) self.assertGreater(subscription_order.next_charge_date, int(one_year_from_now_minus_one_day.strftime('%s')))
def get_subscription_order_remaining_length(customer_id, subscription_order_number): subscription_order = Order.get( Order.create_key(customer_id, subscription_order_number)) if subscription_order.next_charge_date: next_charge_date = subscription_order.next_charge_date + DAY * 14 try: next_charge_datetime = datetime.datetime.utcfromtimestamp( next_charge_date) except ValueError: # Eg. year out of range months_till_charge = 1 else: timedelta = dateutil.relativedelta.relativedelta( next_charge_datetime, datetime.datetime.now()) months_till_charge = timedelta.years * 12 + timedelta.months if months_till_charge < 1: months_till_charge = 1 else: months_till_charge = 1 return months_till_charge, subscription_order
def _add_properties(): customers = Customer.all() to_put = list() for customer in customers: if customer.subscription_order_number: order_key = Order.create_key(customer.id, customer.subscription_order_number) order = Order.get(order_key) customer.creation_time = order.date customer.subscription_type = 0 if OrderItem.all( keys_only=True).ancestor(order_key).filter( 'product_code', 'MSSU').get() else 1 else: customer.creation_time = 0 # at this point it's not known when the customer was created if customer.service_email: service_user = users.User(email=customer.service_email) sln_settings = get_solution_settings(service_user) customer.has_loyalty = True if u'loyalty' in sln_settings.modules else False else: customer.has_loyalty = False to_put.append(customer) db.put(to_put)
def create_and_pay_news_order(service_user, news_item_id, order_items_to): """ Creates an order, orderitems, charge and executes the charge. Should be executed in a transaction. Args: service_user (users.User) news_item_id (long) order_items_to (ist of OrderItemTO) Raises: NoCreditCardException ProductNotFoundException """ @db.non_transactional def _get_customer(): return get_customer(service_user) @db.non_transactional def _get_contact(): return Contact.get_one(customer) customer = _get_customer() azzert(customer) contact = _get_contact() azzert(contact) if not customer.stripe_valid: raise NoCreditCardException(customer) extra_city_product_key = Product.create_key(Product.PRODUCT_EXTRA_CITY) news_product_key = Product.create_key(Product.PRODUCT_NEWS_PROMOTION) rmt_key = RegioManagerTeam.create_key(customer.team_id) extra_city_product, news_promotion_product, team = db.get( (extra_city_product_key, news_product_key, rmt_key)) azzert(extra_city_product) azzert(news_promotion_product) azzert(team) new_order_key = Order.create_key(customer.id, OrderNumber.next(team.legal_entity_key)) vat_pct = get_vat_pct(customer, team) total_amount = 0 added_app_ids = [] for order_item in order_items_to: if order_item.product == Product.PRODUCT_EXTRA_CITY: total_amount += extra_city_product.price * order_item.count added_app_ids.append(order_item.app_id) order_item.price = extra_city_product.price elif order_item.product == Product.PRODUCT_NEWS_PROMOTION: total_amount += news_promotion_product.price * order_item.count order_item.price = news_promotion_product.price else: raise BusinessException('Invalid product \'%s\'' % order_item.product) si = get_default_service_identity(users.User(customer.service_email)) if added_app_ids: keys = [App.create_key(app_id) for app_id in added_app_ids] apps = db.get(keys) for app_id, app in zip(added_app_ids, apps): if not app: raise AppNotFoundException(app_id) if app_id in si.appIds: raise BusinessException('Customer %s already has app_id %s' % (customer.id, app_id)) vat = int(round(vat_pct * total_amount / 100)) total_amount_vat_incl = int(round(total_amount + vat)) now_ = now() to_put = [] order = Order(key=new_order_key, date=now_, amount=total_amount, vat_pct=vat_pct, vat=vat, total_amount=total_amount_vat_incl, contact_id=contact.id, status=Order.STATUS_SIGNED, is_subscription_order=False, is_subscription_extension_order=False, date_signed=now_, manager=STORE_MANAGER, team_id=team.id) to_put.append(order) azzert(order.total_amount >= 0) for item in order_items_to: order_item = OrderItem(parent=new_order_key, number=item.number, product_code=item.product, count=item.count, comment=item.comment, price=item.price) order_item.app_id = item.app_id if order_item.product_code == Product.PRODUCT_NEWS_PROMOTION: order_item.news_item_id = news_item_id to_put.append(order_item) db.put(to_put) # Not sure if this is necessary deferred.defer(generate_and_put_order_pdf_and_send_mail, customer, new_order_key, service_user, _transactional=True) # No need for signing here, immediately create a charge. to_put = [] charge = Charge(parent=new_order_key) charge.date = now() charge.type = Charge.TYPE_ORDER_DELIVERY charge.amount = order.amount charge.vat_pct = order.vat_pct charge.vat = order.vat charge.total_amount = order.total_amount charge.manager = order.manager charge.team_id = order.team_id charge.status = Charge.STATUS_PENDING charge.date_executed = now() charge.currency_code = team.legal_entity.currency_code to_put.append(charge) # Update the regiomanager statistics so these kind of orders show up in the monthly statistics deferred.defer(update_regiomanager_statistic, gained_value=order.amount / 100, manager=order.manager, _transactional=True) # Update the customer service si.appIds.extend(added_app_ids) to_put.append(si) # Update the customer object so the newly added apps are added. customer.app_ids.extend(added_app_ids) customer.extra_apps_count += len(added_app_ids) to_put.append(customer) db.put(to_put) deferred.defer(re_index, si.user) # charge the credit card if charge.total_amount > 0: get_payed(customer.id, order, charge) server_settings = get_server_settings() send_to = server_settings.supportWorkers send_to.append(MC_DASHBOARD.email()) channel_data = { 'customer': customer.name, 'no_manager': True, 'amount': charge.amount / 100, 'currency': charge.currency } channel.send_message(map(users.User, send_to), 'shop.monitoring.signed_order', info=channel_data) else: charge.status = Charge.STATUS_EXECUTED charge.date_executed = now() charge.put() channel.send_message(service_user, 'common.billing.orders.update')
def trans(): charge = None expired_subscription, customer = db.get([ExpiredSubscription.create_key(customer_id), Customer.create_key(customer_id)]) expired_subscription.status = status expired_subscription.status_updated_timestamp = now() if status == ExpiredSubscription.STATUS_WILL_LINK_CREDIT_CARD: # Create a task for regiomanager to check if the customer has linked his credit card after two weeks. # the ExpiredSubscription object from this customer will be cleaned up in recurrentbilling the day after he has linked it. to_put.append(expired_subscription) team, prospect = db.get([RegioManagerTeam.create_key(customer.team_id), Prospect.create_key(customer.prospect_id)]) execution_time = now() + DAY * 14 date_string = datetime.datetime.utcfromtimestamp(execution_time).strftime(u'%A %d %b %Y') comment = u'Check if the customer has linked his creditcard (for automatic subscription renewal).' \ u' If he hasn\'t linked it before %s, contact him again.' % date_string task = create_task(current_user.email(), prospect, team.support_manager, execution_time, ShopTask.TYPE_CHECK_CREDIT_CARD, prospect.app_id, comment=comment) to_put.append(task) elif status == ExpiredSubscription.STATUS_EXTEND_SUBSCRIPTION: # Creates a new charge using the customer his subscription order. subscription_order, team = db.get([Order.create_key(customer.id, customer.subscription_order_number), RegioManagerTeam.create_key(customer.team_id)]) extension_order_item_keys = list() order_items = list(OrderItem.list_by_order(subscription_order.key())) products_to_get = list() for item in order_items: products_to_get.append(Product.create_key(item.product_code)) products = {p.code: p for p in Product.get(products_to_get)} # extend per year months = 12 total_amount = 0 for item in order_items: product = products[item.product_code] if product.is_subscription and item.price > 0: total_amount += months * item.price elif not product.is_subscription and (product.is_subscription_discount or product.extra_subscription_months > 0): total_amount += months * item.price elif product.is_subscription_extension: total_amount += months * item.price extension_order_item_keys.append(item.key()) if total_amount <= 0: raise BusinessException('The created charge has a negative amount (%d)' % total_amount) next_charge_datetime = datetime.datetime.utcfromtimestamp(now()) + relativedelta(months=months) subscription_order.next_charge_date = get_epoch_from_datetime(next_charge_datetime) to_put.append(subscription_order) # reconnect all previously connected friends if the service was disabled in the past if customer.service_disabled_at != 0: deferred.defer(set_service_enabled, customer.id, _transactional=True) vat_pct = get_vat_pct(customer, team) charge = Charge(parent=subscription_order) charge.date = now() charge.type = Charge.TYPE_SUBSCRIPTION_EXTENSION charge.subscription_extension_length = months charge.subscription_extension_order_item_keys = extension_order_item_keys charge.amount = total_amount charge.vat_pct = vat_pct charge.vat = int(total_amount * vat_pct / 100) charge.total_amount = charge.amount + charge.vat charge.currency_code = team.legal_entity.currency_code to_put.append(charge) to_delete.append(expired_subscription) db.put(to_put) if to_delete: db.delete(to_delete) return charge
def trans(): old_order, team = db.get( (old_order_key, RegioManagerTeam.create_key(customer.team_id))) if not old_order: return BoolReturnStatusTO.create( False, translate( get_solution_settings(service_user).main_language, SOLUTION_COMMON, u'cart_empty'), False) # Duplicate the order to_put = list() to_delete = list() properties = copy_model_properties(old_order) properties['status'] = Order.STATUS_SIGNED properties['date_signed'] = now() new_order_key = Order.create_key( customer.id, OrderNumber.next(team.legal_entity_key)) new_order = Order(key=new_order_key, **properties) new_order.team_id = team.id to_delete.append(old_order) # duplicate all of the order items old_order_items = OrderItem.list_by_order(old_order_key) all_products = db.get([ Product.create_key(item.product_code) for item in old_order_items ]) is_subscription_extension_order = False for product in all_products: if product.is_subscription_extension: is_subscription_extension_order = True break new_order.is_subscription_extension_order = is_subscription_extension_order if is_subscription_extension_order: subscription_order = Order.get_by_order_number( customer.id, customer.subscription_order_number) new_order.next_charge_date = subscription_order.next_charge_date added_apps = list() should_create_shoptask = False for old_item in old_order_items: properties = copy_model_properties(old_item) new_item = OrderItem(parent=new_order_key, **properties) to_put.append(new_item) to_delete.append(old_item) if hasattr(old_item, 'app_id'): added_apps.append(old_item.app_id) else: should_create_shoptask = True to_put.append(new_order) db.put(to_put) db.delete(to_delete) deferred.defer(generate_and_put_order_pdf_and_send_mail, customer, new_order_key, service_user, _transactional=True) # No need for signing here, immediately create a charge. azzert(new_order.total_amount > 0) charge = Charge(parent=new_order_key) charge.date = now() charge.type = Charge.TYPE_ORDER_DELIVERY charge.amount = new_order.amount charge.vat_pct = new_order.vat_pct charge.vat = new_order.vat charge.total_amount = new_order.total_amount charge.manager = new_order.manager charge.team_id = new_order.team_id charge.status = Charge.STATUS_PENDING charge.date_executed = now() charge.currency_code = team.legal_entity.currency_code charge.put() # Update the regiomanager statistics so these kind of orders show up in the monthly statistics deferred.defer(update_regiomanager_statistic, gained_value=new_order.amount / 100, manager=new_order.manager, _transactional=True) # Update the customer service si = get_default_service_identity(users.User(customer.service_email)) si.appIds.extend(added_apps) si.put() deferred.defer(re_index, si.user, _transactional=True) # Update the customer object so the newly added apps are added. customer.app_ids.extend(added_apps) customer.extra_apps_count += len(added_apps) customer.put() get_payed(customer.id, new_order, charge) # charge the credit card channel.send_message(service_user, 'common.billing.orders.update') channel_data = { 'customer': customer.name, 'no_manager': True, 'amount': charge.amount / 100, 'currency': charge.currency } server_settings = get_server_settings() send_to = server_settings.supportWorkers send_to.append(MC_DASHBOARD.email()) channel.send_message(map(users.User, send_to), 'shop.monitoring.signed_order', info=channel_data) if should_create_shoptask: prospect_id = customer.prospect_id if prospect_id is None: prospect = create_prospect_from_customer(customer) prospect_id = prospect.id deferred.defer(create_task_for_order, customer.team_id, prospect_id, new_order.order_number, _transactional=True) return BoolReturnStatusTO.create(True, None)
def trans(): to_put = list() customer_store_order_key = Order.create_key( customer.id, Order.CUSTOMER_STORE_ORDER_NUMBER) subscription_order_key = Order.create_key( customer.id, customer.subscription_order_number) team_key = RegioManagerTeam.create_key(customer.team_id) product_key = Product.create_key(item.code) if item.app_id is not MISSING: app_key = App.create_key(item.app_id) product, customer_store_order, sub_order, app, team = db.get([ product_key, customer_store_order_key, subscription_order_key, app_key, team_key ]) if sub_order.status != Order.STATUS_SIGNED: raise BusinessException( translate(lang, SOLUTION_COMMON, u'no_unsigned_order')) # check if the provided app does exist azzert(app) else: product, customer_store_order, team = db.get( [product_key, customer_store_order_key, team_key]) # Check if the item has a correct count. # Should never happen unless the user manually recreates the ajax request.. azzert( not product.possible_counts or item.count in product.possible_counts or item.code == Product.PRODUCT_EXTRA_CITY, u'Invalid amount of items supplied') number = 0 existing_order_items = list() vat_pct = get_vat_pct(customer, team) item_already_added = False if not customer_store_order: # create new order customer_store_order = Order(key=customer_store_order_key) customer_store_order.contact_id = contact.key().id() customer_store_order.date = now() customer_store_order.vat_pct = 0 customer_store_order.amount = 0 customer_store_order.vat = 0 customer_store_order.vat_pct = vat_pct customer_store_order.total_amount = 0 customer_store_order.is_subscription_order = False customer_store_order.manager = STORE_MANAGER customer_store_order.team_id = None else: order_items = OrderItem.list_by_order(customer_store_order.key()) for i in order_items: number = i.number if i.number > number else number existing_order_items.append(i) # Check if this city isn't already in the possible pending order. if hasattr(i, 'app_id') and (i.app_id == item.app_id or item.app_id in customer.app_ids): raise BusinessException( translate(lang, SOLUTION_COMMON, u'item_already_added')) else: # Check if there already is an orderitem with the same product code. # If so, add the count of this new item to the existing item. for it in order_items: if it.product_code == item.code and it.product_code not in ( Product.PRODUCT_EXTRA_CITY, Product.PRODUCT_NEWS_PROMOTION): if ( it.count + item.count ) in product.possible_counts or not product.possible_counts: it.count += item.count item_already_added = True to_put.append(it) order_item = it elif len(product.possible_counts) != 0: raise BusinessException( translate( lang, SOLUTION_COMMON, u'cant_order_more_than_specified', allowed_items=max( product.possible_counts))) if item.app_id is not MISSING: remaining_length, _ = get_subscription_order_remaining_length( customer.id, customer.subscription_order_number) subscription_order_charge_date = format_date( datetime.datetime.utcfromtimestamp(sub_order.next_charge_date), locale=lang) total = remaining_length * product.price else: total = product.price * item.count vat = total * vat_pct / 100 total_price = total + vat customer_store_order.amount += total customer_store_order.vat += vat azzert(customer_store_order.total_amount >= 0) customer_store_order.total_amount += total_price service_visible_in_translation = None if not item_already_added: order_item = OrderItem(parent=customer_store_order.key()) order_item.number = number order_item.comment = product.default_comment(customer.language) order_item.product_code = product.code if item.app_id is not MISSING: order_item.count = remaining_length service_visible_in_translation = translate( lang, SOLUTION_COMMON, 'service_visible_in_app', subscription_expiration_date=subscription_order_charge_date, amount_of_months=remaining_length, extra_city_price=product.price_in_euro, app_name=app.name) else: order_item.count = item.count order_item.price = product.price if item.app_id is not MISSING: order_item.app_id = item.app_id to_put.append(order_item) to_put.append(customer_store_order) db.put(to_put) return order_item, service_visible_in_translation