def _job():
    products = {p.code: p for p in Product.all()}
    order_keys = set()
    for order_item in OrderItem.all():
        if products[order_item.product_code].is_subscription_extension:
            order_keys.add(order_item.order_key)

    orders = db.get(order_keys)
    to_put = list()
    for order in orders:
        if not order.is_subscription_extension_order:
            order.is_subscription_extension_order = True
            customer = order.parent()
            subscription_order = Order.get_by_order_number(customer.id, customer.subscription_order_number)
            order.next_charge_date = subscription_order.next_charge_date
            to_put.append(order)

    for chunk in chunks(to_put, 200):
        db.put(chunk)
Esempio n. 2
0
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)
Esempio n. 3
0
def _replace_subscription_order(order_key, products,
                                paying_subscription_product_codes):
    customer, old_order = db.get([order_key.parent(), order_key])

    if not is_signup_enabled(customer.app_id):
        logging.debug(
            'FREE_SUBSCRIPTIONS - Signup is not enabled for customer %s with app %s',
            customer.name, customer.app_id)
        return

    if customer.service_disabled_at != 0:
        logging.debug('FREE_SUBSCRIPTIONS - Customer %s is disabled',
                      customer.name)
        return

    if old_order.status == Order.STATUS_SIGNED:
        order_items = list(OrderItem.all().ancestor(old_order))
        ordered_product_codes = {i.product_code for i in order_items}
        if not ordered_product_codes.intersection(
                paying_subscription_product_codes):
            logging.debug(
                'FREE_SUBSCRIPTIONS - Customer %s already had a FREE subscription: %s',
                customer.name, list(ordered_product_codes))
            return

        logging.debug(
            'FREE_SUBSCRIPTIONS - Creating new FREE order for customer %s',
            customer.name)
        new_order_items = []
        for old_order_item in OrderItem.list_by_order(order_key):
            product = products[old_order_item.product_code]
            if product.is_subscription_extension:
                new_order_items.append(OrderItemTO.create(old_order_item))
        if new_order_items:
            logging.debug(
                'FREE_SUBSCRIPTIONS - Adding %s old order items: %s',
                len(new_order_items),
                serialize_complex_value(new_order_items, OrderItemTO, True))

        free_item = OrderItemTO()
        free_item.comment = products[
            Product.PRODUCT_FREE_SUBSCRIPTION].default_comment(
                customer.language)

        next_charge_date_time = datetime.datetime.utcfromtimestamp(
            old_order.next_charge_date)
        language = 'nl' if customer.language == 'nl' else 'en'
        next_charge_date = format_date(next_charge_date_time, locale=language)
        if language == 'nl':
            free_item.comment += u'\n\nEr zijn geen abonnementskosten meer! Uw abonnement is omgezet naar een gratis abonnement.'
            if new_order_items:
                free_item.comment += u'\n\nUw uitbreiding voor extra stad/steden is mee overgezet naar het nieuwe abonnement en zal, zoals bij het oude abonnement, op %s aangerekend worden.' % next_charge_date
        else:
            free_item.comment = u'There are no more subscription costs! Your subscription is changed to a free subscription'
            if new_order_items:
                free_item.comment += u'\n\nThe extension for extra cities is also transferred to the new subscription and will be charged at %s, just like you old subscription.' % next_charge_date

        free_item.count = 1
        free_item.id = None
        free_item.number = 0
        free_item.price = 0
        free_item.product = Product.PRODUCT_FREE_SUBSCRIPTION
        free_item.service_visible_in = None
        new_order_items.append(free_item)

        new_order = create_order(customer,
                                 old_order.contact_id,
                                 new_order_items,
                                 replace=True,
                                 regio_manager_user=old_order.manager)
        new_order.next_charge_date = old_order.next_charge_date
        new_order.put()
    else:
        bizz_check(
            customer.subscription_order_number != old_order.order_number,
            'Something is seriously wrong with customer %s (%s)!' %
            (customer.id, customer.name))
        new_order = Order.get_by_order_number(
            customer.id, customer.subscription_order_number)

    if new_order.status == Order.STATUS_UNSIGNED and new_order.total_amount > 0:
        logging.debug('FREE_SUBSCRIPTIONS - Signing order %s for customer %s',
                      new_order.order_number, customer.name)
        sign_order(customer.id,
                   new_order.order_number,
                   signature=u'',
                   no_charge=True)
Esempio n. 4
0
def export_invoices(year, month):
    start_date = datetime.date(year, month, 1)
    end_date = start_date + relativedelta(months=1)

    qry = Invoice.all() \
        .filter('date >=', get_epoch_from_datetime(start_date)) \
        .filter('date <', get_epoch_from_datetime(end_date))

    invoices = list()
    order_keys = set()
    all_products = dict(((p.code, p) for p in Product.all()))
    for invoice_model in qry:
        i = model_to_dict(invoice_model)
        order_key = invoice_model.parent_key().parent()
        i['invoice_number'] = invoice_model.invoice_number
        i['order_items'] = map(model_to_dict,
                               OrderItem.all().ancestor(order_key))
        if invoice_model.charge.is_recurrent:
            # only apply recurrent charges
            for order_item in reversed(i['order_items']):
                order_item[
                    'count'] = invoice_model.charge.subscription_extension_length or 1
                product = all_products[order_item['product_code']]
                if not (product.is_subscription_discount
                        or product.is_subscription
                        or product.is_subscription_extension):
                    i['order_items'].remove(order_item)

            # add the subscription extensions like XCTY
            if invoice_model.charge.subscription_extension_order_item_keys:
                known_extension_item_keys = [
                    item['_key'] for item in i['order_items']
                ]

                extension_order_items = db.get(
                    invoice_model.charge.subscription_extension_order_item_keys
                )
                for item in extension_order_items:
                    item.count = 1
                    if str(item.key()) not in known_extension_item_keys:
                        i['order_items'].append(model_to_dict(item))

        i['order_key'] = order_key
        i['currency'] = invoice_model.currency_code
        order_keys.add(order_key)
        invoices.append(i)

    orders = {o.key(): o for o in db.get(order_keys)}

    contact_keys = set()
    customer_keys = set()
    for i in invoices:
        order_model = orders[i['order_key']]
        del i['order_key']
        i['customer_key'] = order_model.customer_key
        i['contact_key'] = order_model.contact_key
        i['manager'] = None if not order_model.manager else order_model.manager.email(
        )
        customer_keys.add(order_model.customer_key)
        contact_keys.add(order_model.contact_key)

    del orders

    customer_and_contact_models = {
        m.key(): m
        for m in db.get(customer_keys.union(contact_keys))
    }

    # filter invoices for customers of resellers
    reseller_ids = [
        k.id() for k in LegalEntity.list_non_mobicage(keys_only=True)
    ]
    reseller_team_ids = [
        t.id for t in RegioManagerTeam.all().filter('legal_entity_id IN',
                                                    reseller_ids)
    ]

    for i in reversed(invoices):
        customer_model = customer_and_contact_models[i['customer_key']]
        if customer_model.team_id in reseller_team_ids:
            invoices.remove(i)
            continue
        del i['customer_key']
        i['customer'] = model_to_dict(customer_model)
        contact_model = customer_and_contact_models[i['contact_key']]
        del i['contact_key']
        i['contact'] = model_to_dict(contact_model)

    del customer_and_contact_models

    return sorted(invoices,
                  key=lambda i: int(i['invoice_number'].split('.')[-1]))
Esempio n. 5
0
    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