def create_task_if_not_order(customer_id): customer = Customer.get_by_id(customer_id) # Check if the customer has linked his credit card after he clicked the 'pay' button # If he didn't (one hour after the last time he tried to pay), create a new ShopTask to call this customer. if not customer.stripe_valid and customer.team_id: if customer.prospect_id: rmt, prospect = db.get([ RegioManagerTeam.create_key(customer.team_id), Prospect.create_key(customer.prospect_id) ]) else: prospect = create_prospect_from_customer(customer) rmt = RegioManagerTeam.get( RegioManagerTeam.create_key(customer.team_id)) azzert(rmt.support_manager, 'No support manager found for team %s' % rmt.name) task = create_task( prospect_or_key=prospect, status=ShopTask.STATUS_NEW, task_type=ShopTask.TYPE_SUPPORT_NEEDED, address=None, created_by=STORE_MANAGER.email(), assignee=rmt.support_manager, execution_time=today() + 86400 + 11 * 3600, # tomorrow, 11:00, app_id=prospect.app_id, comment=u"Customer wanted to pay an order in the customer store, " "but didn't succeed because he did not link his creditcard.", notify_by_email=True) task.put()
def trans(): prospect = Prospect(key_name=place_id, name=name, app_id=app.app_id, type=[], categories=[u'unclassified'], address=address, geo_point=db.GeoPt(lat, lon), assignee=assignee, status=Prospect.STATUS_ADDED_BY_DISCOVERY, phone='' ) task = create_task(assignee, Prospect.create_key(place_id), assignee, now() + 86400, ShopTask.TYPE_CALL, app.app_id, ShopTask.STATUS_NEW, address, u'Created prospect via discovery', None, None) db.put([prospect, task]) return prospect
def job(cursor=None): qry = Prospect.all() if cursor: qry.with_cursor(cursor) prospects = qry.fetch(200) if not prospects: return to_put = list() index = search.Index(name=PROSPECT_INDEX) for prospect in prospects: try: index.delete(prospect.id) except ValueError: pass fields = [ search.AtomField(name='key', value=prospect.id), search.TextField(name='name', value=prospect.name), search.TextField(name='address', value=prospect.address), search.TextField(name='phone', value=prospect.phone), search.TextField(name='email', value=prospect.email) ] doc = search.Document(doc_id=prospect.id, fields=fields) to_put.append(doc) index.put(to_put) deferred.defer(job, qry.cursor())
def _unassign_prospects(manager_email): logging.info('Unassigning regional manager %s from all prospects', manager_email) to_put = [] for prospect in Prospect.find_by_assignee(manager_email).fetch(None): prospect.assignee = None to_put.append(prospect) put_in_chunks(to_put)
def job(cursor=None): qry = Prospect.all() if cursor: qry.with_cursor(cursor) prospects = qry.fetch(200) if not prospects: return for p in prospects: if p.status == Prospect.STATUS_ADDED_BY_DISCOVERY: p.categories = ['unclassified'] else: p.categories = Prospect.convert_place_types(p.type) db.put(prospects) deferred.defer(job, qry.cursor())
def search_prospects(query): """ Uses the PROSPECT_INDEX search index to search for prospects based on their name, address, phone number and email. Args: query: The search query Returns: list(Prospect): List of prospects that match the search query. """ if not query: return [] search_index = search.Index(name=PROSPECT_INDEX) q = normalize_search_string(query) query = search.Query(query_string=q, options=search.QueryOptions(limit=20)) search_result = search_index.search(query) prospect_keys = [Prospect.create_key(long(result.doc_id) if result.doc_id.isdigit() else result.doc_id) for result in search_result.results] return Prospect.get(prospect_keys)
def job(cursor=None): qry = Prospect.all() if cursor: qry.with_cursor(cursor) prospects = qry.fetch(200) if not prospects: return for p in prospects: p.categories = list(set(p.categories)) db.put(prospects) deferred.defer(job, qry.cursor())
def create_task_for_order(customer_team_id, prospect_id, order_number): team, prospect = db.get([ RegioManagerTeam.create_key(customer_team_id), Prospect.create_key(prospect_id) ]) azzert(team.support_manager, u'No support manager found for team %s' % team.name) comment = u'Customer placed a new order: %s' % (order_number) task = create_task( created_by=STORE_MANAGER.email(), prospect_or_key=prospect, app_id=prospect.app_id, status=ShopTask.STATUS_NEW, task_type=ShopTask.TYPE_SUPPORT_NEEDED, address=None, assignee=team.support_manager, comment=comment, execution_time=today() + 86400 + 11 * 3600, # tomorrow at 11:00 notify_by_email=True) task.put() broadcast_task_updates([team.support_manager])
def trans(): prospect = Prospect.get_by_key_name(place_id) if prospect: logging.debug('There already was a prospect for place %s', place_id) return prospect = Prospect( key_name=place_id, app_id=app_id, name=result['name'], type=result['types'], categories=Prospect.convert_place_types(result['types']), address=address, geo_point=db.GeoPt(location['lat'], location['lng']), phone=phone_number, website=result.get('website')) prospect.put() deferred.defer(re_index_prospect, prospect, _transactional=True) deferred.defer(broadcast_prospect_creation, None, prospect, _transactional=True)
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 _get_all_prospects(): return Prospect.all()
def generate_prospect_export_excel(prospect_ids, do_send_email=True, recipients=None): if not prospect_ids: raise BusinessException('No prospects to export') azzert(not do_send_email or recipients) bold_style = xlwt.XFStyle() bold_style.font.bold = True column_name, column_address, column_city, column_phone, column_status, column_type, column_categories, column_comments = range(8) book = xlwt.Workbook(encoding="utf-8") sheet = book.add_sheet('Prospects') prospects = Prospect.get(map(Prospect.create_key, prospect_ids)) app_id = None sheet.write(0, column_name, 'Name', bold_style) sheet.write(0, column_address, 'Address', bold_style) sheet.write(0, column_city, 'City', bold_style) sheet.write(0, column_phone, 'Phone', bold_style) sheet.write(0, column_status, 'Status', bold_style) sheet.write(0, column_type, 'Type', bold_style) sheet.write(0, column_categories, 'Category', bold_style) sheet.write(0, column_comments, 'Comments', bold_style) for i, prospect in enumerate(prospects): row = i + 1 comments_str = '\n'.join(['* %s' % comment.text for comment in prospect.comments]) sheet.write(row, column_name, prospect.name) formatted_address = format_address(prospect.address) sheet.write(row, column_address, formatted_address[0]) sheet.write(row, column_city, formatted_address[1]) sheet.write(row, column_phone, prospect.phone) sheet.write(row, column_status, Prospect.STATUS_TYPES[prospect.status]) sheet.write(row, column_type, ', '.join(prospect.type)) sheet.write(row, column_categories, ', '.join(prospect.categories)) sheet.write(row, column_comments, comments_str) sheet.col(column_name).width = 5000 sheet.col(column_address).width = 5000 sheet.col(column_phone).width = 5000 sheet.col(column_status).width = 5000 sheet.col(column_type).width = 10000 sheet.col(column_categories).width = 10000 sheet.col(column_comments).width = 20000 if not app_id: app_id = prospect.app_id excel = StringIO() book.save(excel) excel_string = excel.getvalue() if do_send_email: app = get_app_by_id(app_id) solution_server_settings = get_solution_server_settings() current_date = format_datetime(datetime.datetime.now(), locale=DEFAULT_LANGUAGE) subject = 'Exported prospects of %s %s' % (app.name, current_date) from_email = solution_server_settings.shop_export_email to_emails = recipients body_text = 'See attachment for the exported prospects' attachments = [] attachments.append(('Prospects %s %s.xls' % (app.name, current_date), base64.b64encode(excel_string))) send_mail(from_email, to_emails, subject, body_text, attachments=attachments) return excel_string
def create_prospect_from_customer(customer): azzert(customer.prospect_id is None and customer.service_email) contact = Contact.get_one(customer.key()) azzert(contact) si = get_default_service_identity(users.User(customer.service_email)) prospect = Prospect(key_name=str(uuid.uuid4()) + str(uuid.uuid4())) prospect.name = customer.name prospect.address = ', '.join(filter(None, [customer.address1 + ((' ' + customer.address2) if customer.address2 else ''), customer.zip_code, customer.city, OFFICIALLY_SUPPORTED_COUNTRIES.get(customer.country, customer.country)])) prospect.phone = contact.phone_number prospect.email = contact.email prospect.type = ['establishment'] if customer.organization_type == OrganizationType.EMERGENCY: prospect.type.append('health') prospect.customer_id = customer.id prospect.status = Prospect.STATUS_CUSTOMER prospect.app_id = si.app_id solution_server_settings = get_solution_server_settings() prospect.add_comment(u'Converted customer to prospect', users.User(solution_server_settings.shop_no_reply_email)) try: result = geo_code(prospect.address) except GeoCodeZeroResultsException: try: result = geo_code(' '.join(filter(None, [customer.zip_code, customer.city, OFFICIALLY_SUPPORTED_COUNTRIES.get(customer.country, customer.country)]))) except GeoCodeZeroResultsException: logging.warn('Could not geo_code customer: %s', db.to_dict(customer)) return prospect.geo_point = db.GeoPt(result['geometry']['location']['lat'], result['geometry']['location']['lng']) customer.prospect_id = prospect.id prospect.customer_id = customer.id logging.info('Creating prospect: %s', db.to_dict(prospect, dict(prospect_id=prospect.id))) put_and_invalidate_cache(customer, prospect) return prospect
def trans(): customer_id = order_key.parent().id() order, customer = db.get([order_key, Customer.create_key(customer_id)]) if not order.next_charge_date: logging.warning( 'Not creating recurrent charge for order %s (%s: %s) because no next charge date is set', order.order_number, customer_id, customer.name) return None elif order.next_charge_date > today: # Scenario: this job fails today, tomorrow this job runs again and fails again # -> 2 jobs for the same order would create 2 charges when the bug is fixed logging.warning( 'This order has already been charged this month, skipping... %s (%s: %s)', order.order_number, customer_id, customer.name) return None elif customer.subscription_cancel_pending_date: logging.info('Disabling service from customer %s (%d)', customer.name, customer.id) try: cancel_order(customer, order.order_number) except OrderAlreadyCanceledException as exception: logging.info('Order %s already canceled, continuing...', exception.order.order_number) set_service_disabled(customer, Customer.DISABLED_OTHER) cleanup_expired_subscription(customer) return None logging.info("Creating recurrent charge for order %s (%s: %s)", order.order_number, customer_id, customer.name) subscription_extension_orders = list( Order.all().ancestor(customer).filter( "next_charge_date <", today).filter("is_subscription_order =", False).filter( 'is_subscription_extension_order =', True).filter("status =", Order.STATUS_SIGNED)) # type: list[Order] subscription_extension_order_keys = [ o.key() for o in subscription_extension_orders ] order_item_qry = OrderItem.all().ancestor( customer if subscription_extension_order_keys else order) subscription_extension_order_item_keys = [] total_amount = 0 subscription_length = 0 current_date = datetime.datetime.utcnow() to_put = [] for order_item in order_item_qry: # type: OrderItem product = products[order_item.product_code] if order_item.order_number == order.order_number: if product.is_subscription: subscription_length = order_item.count if product.is_subscription or product.is_subscription_discount or product.is_subscription_extension: if product.charge_interval != 1: last_charge_date = datetime.datetime.utcfromtimestamp( order_item.last_charge_timestamp) new_charge_date = last_charge_date + relativedelta( months=product.charge_interval) if new_charge_date < current_date: logging.debug( 'new_charge_date %s < current_date %s, adding %s to total_amount', new_charge_date, current_date, order_item.price) total_amount += order_item.price order_item.last_charge_timestamp = now() to_put.append(order_item) else: total_amount += order_item.price elif order_item.parent().key( ) in subscription_extension_order_keys: if product.is_subscription_extension: total_amount += order_item.price subscription_extension_order_item_keys.append( order_item.key()) if total_amount == 0: order.next_charge_date = Order.default_next_charge_date() order.put() logging.info( "Skipping, cannot calculate recurrent charge of 0 euros for order %s (%s: %s)", order.order_number, customer_id, customer.name) return None if subscription_length == 0: raise Exception('subscription_length is 0') if not (customer.stripe_id and customer.stripe_credit_card_id) and subscription_length != 1: logging.debug( 'Tried to bill customer, but no credit card info was found') audit_log( customer.id, 'Tried to bill customer, but no credit card info was found') # Log the customer as expired. If this has not been done before. expired_subscription_key = ExpiredSubscription.create_key( customer_id) if not ExpiredSubscription.get(expired_subscription_key): to_put.append( ExpiredSubscription( key=expired_subscription_key, expiration_timestamp=order.next_charge_date)) # Create a task for the support manager assignee = customer.manager and customer.manager.email() if customer.team_id is not None: team = RegioManagerTeam.get_by_id(customer.team_id) if team.support_manager: assignee = team.support_manager if assignee: if customer.prospect_id: prospect = Prospect.get( Prospect.create_key(customer.prospect_id)) else: # We can only create tasks for prospects. So we must create a prospect if there was none. prospect = create_prospect_from_customer(customer) customer.prospect_id = prospect.id to_put.append(customer) to_put.append(prospect) to_put.append( create_task( created_by=None, prospect_or_key=prospect, assignee=assignee, execution_time=today + 11 * 3600, task_type=ShopTask.TYPE_SUPPORT_NEEDED, app_id=prospect.app_id, status=ShopTask.STATUS_NEW, comment= u"Customer needs to be contacted for subscription renewal", notify_by_email=True)) put_and_invalidate_cache(*to_put) return None else: cleanup_expired_subscription(customer) @db.non_transactional # prevent contention on entity group RegioManagerTeam def get_currency_code(): return customer.team.legal_entity.currency_code charge = Charge(parent=order_key) charge.date = now() charge.type = Charge.TYPE_RECURRING_SUBSCRIPTION charge.subscription_extension_length = 1 charge.subscription_extension_order_item_keys = subscription_extension_order_item_keys charge.currency_code = get_currency_code() charge.team_id = customer.team_id charge.amount = total_amount charge.vat_pct = order.vat_pct charge.vat = int(total_amount * order.vat_pct / 100) charge.total_amount = charge.amount + charge.vat to_put.append(charge) next_charge_datetime = datetime.datetime.utcfromtimestamp( order.next_charge_date) + relativedelta(months=1) next_charge_date_int = int( (next_charge_datetime - datetime.datetime.utcfromtimestamp(0)).total_seconds()) order.next_charge_date = next_charge_date_int to_put.append(order) for extension_order in subscription_extension_orders: extension_order.next_charge_date = next_charge_date_int to_put.append(extension_order) put_and_invalidate_cache(*to_put) return charge