def _job():
    legal_entity = get_mobicage_legal_entity()
    if not legal_entity:
        legal_entity = create_mobicage_legal_entity()
    to_put = list()
    to_delete = list()
    for invoice_number in InvoiceNumber.all():
        copied_object = InvoiceNumber(key_name=invoice_number.key().name(),
                                      parent=legal_entity,
                                      last_number=invoice_number.last_number)
        to_put.append(copied_object)
        to_delete.append(invoice_number)
    for order_number in OrderNumber.all():
        copied_object = OrderNumber(key_name=order_number.key().name(),
                                    parent=legal_entity,
                                    last_number=order_number.last_number)
        to_put.append(copied_object)
        to_delete.append(order_number)
    for charge_number in ChargeNumber.all():
        copied_object = OrderNumber(key_name=charge_number.key().name(),
                                    parent=legal_entity,
                                    last_number=charge_number.last_number)
        to_put.append(copied_object)
        to_delete.append(charge_number)

    def trans():
        db.delete(to_delete)
        db.put(to_put)

    run_in_xg_transaction(trans)
def send_welcome_message(user):
    def trans():
        signup_url = get_server_settings().signupUrl
        if not signup_url:
            logging.error('Will not send the welcome message, signup URL is not set')
            return

        ui = UserInteraction.get_by_key_name(user.email(), parent=parent_key(user)) or UserInteraction(
            key_name=user.email(), parent=parent_key(user))
        if not is_flag_set(UserInteraction.INTERACTION_WELCOME, ui.interactions):
            ui.interactions |= UserInteraction.INTERACTION_WELCOME
            db.put_async(ui)
            user_profile = get_user_profile(user)
            service_identity_user = add_slash_default(MC_DASHBOARD)
            parent_message_key = None
            app = get_app_by_id(user_profile.app_id)
            if app.type in (App.APP_TYPE_ENTERPRISE, App.APP_TYPE_YSAAA, App.APP_TYPE_CONTENT_BRANDING, App.APP_TYPE_SECURE_DEVICE):
                return None
            msg = localize_app_translation(user_profile.language, '_welcome_message', app.app_id, app_name=app.name,
                                           contact_email_address=app.get_contact_email_address())
            xml = generate_welcome_message_flow(user_profile, app, msg, signup_url)
            members = [user]
            push_message = msg.splitlines()[0]
            start_local_flow(service_identity_user, parent_message_key, xml, members, push_message=push_message,
                             check_friends=False)

    run_in_xg_transaction(trans)
def _set_secure_info(app_user, current_mobile, public_key, public_keys):
    def trans():

        user_profile = get_user_profile(app_user)
        if public_keys is not MISSING:
            changed_properties = [u"public_keys"]
            if not user_profile.public_keys:
                user_profile.public_keys = PublicKeys()
            for pk in public_keys:
                if pk.public_key:
                    user_profile.public_keys.addNew(pk.algorithm, pk.name, pk.index, pk.public_key)
                    name = PublicKeyHistory.create_name(pk.algorithm, pk.name, pk.index)
                    pk = PublicKeyHistory.create(app_user, name, pk.public_key)
                    pk.put()
                else:
                    user_profile.public_keys.remove(pk.algorithm, pk.name, pk.index)
        else:
            changed_properties = [u"public_key"]
            user_profile.public_key = public_key
            pk = PublicKeyHistory.create(app_user, None, public_key)
            pk.put()
        user_profile.version += 1
        user_profile.put()

        update_friend_service_identity_connections(user_profile.key(), changed_properties)  # trigger friend.update

    run_in_xg_transaction(trans)
Example #4
0
def _gather_events(cap_key):
    def trans():
        si_key = ServiceIdentity.keyFromService(
            users.User(cap_key.parent().name()), ServiceIdentity.DEFAULT)
        cap, si = db.get([cap_key, si_key])
        if cap is None or si is None:
            return
        if cap.gather_events:
            cap.gather_events.clear()
        else:
            cap.gather_events = KVStore(cap_key)
        organization_types = CityAppProfile.EVENTS_ORGANIZATION_TYPES
        for org_type in organization_types:
            with closing(StringIO()) as stream:
                json.dump([], stream)
                cap.gather_events[unicode(org_type)] = stream
        cap.put()
        if cap.gather_events_enabled:
            for org_type in organization_types:
                on_trans_committed(run_job,
                                   _get_customers_by_organization_type,
                                   [org_type, si.app_id],
                                   _gather_events_for_customer,
                                   [cap_key, org_type])

    run_in_xg_transaction(trans)
Example #5
0
def put_customer_service(customer,
                         service,
                         search_enabled,
                         skip_module_check,
                         skip_email_check,
                         rollback=False):
    """Put the service, if rolllback is set, it will remove the customer in case of failure."""
    from shop.bizz import put_service
    customer_key = customer.key()

    def trans():
        put_service(customer,
                    service,
                    skip_module_check=skip_module_check,
                    search_enabled=search_enabled,
                    skip_email_check=skip_email_check)

    try:
        run_in_xg_transaction(trans)
    except Exception as e:
        if rollback:
            db.delete(
                db.GqlQuery("SELECT __key__ WHERE ANCESTOR IS KEY('%s')" %
                            customer_key).fetch(None))
        logging.debug('Unhandled exception: %s', e, exc_info=True)
        raise e
Example #6
0
def store_stripe_link_to_datastore(customer_key, stripe_customer_id,
                                   stripe_card_id, stripe_token_created,
                                   contact_id):
    def trans():
        customer = Customer.get(customer_key)

        if customer.stripe_id:
            if customer.stripe_credit_card_id:
                old_cc = CreditCard.get_by_key_name(
                    customer.stripe_credit_card_id, parent=customer)
                old_cc.deleted = True
                old_cc.put()
        else:
            customer.stripe_id = stripe_customer_id

        cc = CreditCard(key_name=stripe_card_id, parent=customer)
        cc.creation_time = stripe_token_created
        cc.contact_id = contact_id
        cc.put()

        customer.stripe_id_created = stripe_token_created
        customer.stripe_credit_card_id = stripe_card_id
        customer.put()

    run_in_xg_transaction(trans)
def _job():
    order_numbers = list(OrderNumber.all())
    charge_numbers = list(ChargeNumber.all())
    invoice_numbers = list(InvoiceNumber.all())
    # Change parent from some OrderNumbers from RegioMangerTeam to LegalEntity.
    # Increase last_number for that legal entity when the parent was a team.
    to_delete = list()
    for ordernumber in order_numbers:
        if ordernumber.parent_key().kind() == RegioManagerTeam.kind():
            # Delete OrderNumber with parent RegioManagerTeam
            logging.warn(
                "Deleting OrderNumber Year %s Original last number: %s" %
                (ordernumber.key().name(), ordernumber.last_number))
            to_delete.append(ordernumber)

    for chargenumber in charge_numbers:
        if chargenumber.parent_key().kind() == RegioManagerTeam.kind():
            logging.warn(
                "Deleting ChargeNumber Year %s Original last number: %s" %
                (chargenumber.key().name(), chargenumber.last_number))
            to_delete.append(chargenumber)

    for invoicenumber in invoice_numbers:
        if invoicenumber.parent_key().kind() == RegioManagerTeam.kind():
            logging.warn(
                "Deleting InvoiceNumber Year %s Original last number: %s" %
                (invoicenumber.key().name(), invoicenumber.last_number))
            # Delete InvoiceNumber with parent RegioManagerTeam
            to_delete.append(invoicenumber)
    db.delete(to_delete)

    for invoice in Invoice.all().filter(
            'date >', 1460635200
            if not DEBUG else 1459870047):  # Thu, 14 Apr 2016 12:00:00 GMT

        def trans():
            new_invoice = Invoice(key_name=InvoiceNumber.next(
                LegalEntity.create_key(invoice.legal_entity_id)),
                                  parent=invoice.parent_key())
            logging.warn("Creating new Invoice %s" % new_invoice.key().name())
            new_invoice.date = invoice.date
            new_invoice.pdf = invoice.pdf
            new_invoice.amount = invoice.amount
            new_invoice.vat_pct = invoice.vat_pct
            new_invoice.total_amount = invoice.total_amount
            new_invoice.paid = invoice.paid
            new_invoice.paid_timestamp = invoice.paid_timestamp
            new_invoice.payment_type = invoice.payment_type
            new_invoice.operator = invoice.operator
            new_invoice.legal_entity_id = invoice.legal_entity_id
            new_invoice.put()
            invoice.deleted = True
            invoice.put()

        run_in_xg_transaction(trans)
Example #8
0
def migrate_avatar(sln_settings_key):
    service_user = users.User(sln_settings_key.id_or_name())

    def trans():
        service_profile = get_service_profile(service_user)
        if not service_profile:
            return
        sln_avatar = SolutionAvatar(
            key=SolutionAvatar.create_key(service_user))
        sln_avatar.picture = get_avatar_by_id(service_profile.avatarId).picture
        sln_avatar.put()

    run_in_xg_transaction(trans)
Example #9
0
def start_migration_for_solution_services(sln_settings_key):
    service_user = users.User(sln_settings_key.id_or_name())

    def trans():
        mfd_list = [
            mfd for mfd in get_message_flow_designs_by_status(
                service_user, MessageFlowDesign.STATUS_VALID)
            if mfd.xml and not mfd.definition
        ]  # XML-only flows
        render_js_for_message_flow_designs(mfd_list, notify_friends=False)
        for chunk in chunks(mfd_list, 200):
            put_and_invalidate_cache(*chunk)
        schedule_update_all_friends_of_service_user(service_user)

    run_in_xg_transaction(trans)
Example #10
0
def generate_and_put_order_pdf_and_send_mail(customer, new_order_key,
                                             service_user):
    def trans():
        new_order = Order.get(new_order_key)
        with closing(StringIO()) as pdf:
            generate_order_or_invoice_pdf(pdf, customer, new_order)
            new_order.pdf = pdf.getvalue()

        new_order.put()
        deferred.defer(send_order_email,
                       new_order_key,
                       service_user,
                       _transactional=True)

    run_in_xg_transaction(trans)
Example #11
0
def set_customer_signup_status(city_customer, signup, approved, reason=None):
    """Set the customer signup to done and move the inbox message to trash"""
    def trans():
        to_put = [signup]
        inbox_message = None
        signup.done = approved
        if signup.inbox_message_key:
            inbox_message = db.get(signup.inbox_message_key)
            if inbox_message:
                if approved:
                    inbox_message.trashed = True
                else:
                    inbox_message.read = True
                to_put.append(inbox_message)

        db.put(to_put)
        return inbox_message

    message = run_in_xg_transaction(trans)
    if message:
        service_user = signup.parent().service_user
        sln_settings = get_solution_settings(service_user)

        if approved:
            _send_approved_signup_email(signup)
        else:
            _send_denied_signup_email(city_customer, signup,
                                      sln_settings.main_language, reason)

        status_reply_message = _send_signup_status_inbox_reply(
            sln_settings, signup.inbox_message_key, approved, reason)
        send_signup_update_messages(sln_settings, message,
                                    status_reply_message)
    def get(self):
        data_dict, app_user = self.get_user_info()
        if not data_dict or not app_user:
            return
        azzert(data_dict['a'] == "unsubscribe broadcast")

        broadcast_type = data_dict['bt']
        si_user = users.User(data_dict['e'])

        _, user_profile, si, fsic = run_in_xg_transaction(self._un_subscribe, app_user, si_user, broadcast_type)

        if fsic or not si:
            message = '%s,<br><br>%s' % (xml_escape(localize(user_profile.language, u'dear_name',
                                                             name=user_profile.name)),
                                         xml_escape(localize(user_profile.language,
                                                             u'successfully_unsubscribed_broadcast_type',
                                                             notification_type=broadcast_type,
                                                             service=si.name if si else data_dict['n'])))
        else:
            language = get_languages_from_request(self.request)[0]
            if not user_profile:
                # User already deactivated his account
                human_user, app_id = get_app_user_tuple(app_user)
                message = localize(language, u'account_already_deactivated',
                                   account=human_user.email(), app_name=get_app_name_by_id(app_id))
            else:
                # User is not connected anymore to this service identity
                message = localize(language, u'account_already_disconnected_from_service',
                                   service_name=si.name)

        jinja_template = self.get_jinja_environment().get_template('unsubscribe_broadcast_type.html')
        self.response.out.write(jinja_template.render(dict(message=message)))
def create_app(data):
    """
    Args:
        data (CreateAppTO)
    Returns:
        App: The newly created app
    """
    if not is_valid_app_id(data.app_id):
        raise InvalidAppIdException(data.app_id)

    def trans(create_app_to):
        app_key = App.create_key(create_app_to.app_id)
        if App.get(app_key):
            raise DuplicateAppIdException(create_app_to.app_id)
        app = App(
            key=app_key,
            type=data.type,
            name=create_app_to.name,
            is_default=False,
            android_app_id=u'com.mobicage.rogerthat.%s' % create_app_to.app_id.replace('-', '.'),
            dashboard_email_address=data.dashboard_email_address,
            creation_time=now(),
        )
        app_settings = AppSettings(key=AppSettings.create_key(app.app_id),
                                   background_fetch_timestamps=[21600] if app.type == App.APP_TYPE_CITY_APP else [])
        db.put((app, app_settings))
        return app

    app = run_in_xg_transaction(trans, data)
    if data.auto_added_services not in (None, MISSING):
        schedule_add_app_id_to_services(app.app_id, data.auto_added_services)

    return app
Example #14
0
def redeem_news_coupon(coupon_id, service_identity_user, redeeming_user):
    """
    Args:
        coupon_id (int): id of the coupon
        service_identity_user (users.User): service user for which the coupon is valid
        redeeming_user (users.User): user that used/wants to use the coupon

    Raises:
        NewsCouponNotFoundException
        NewsCouponAlreadyUsedException: When the news coupon has already been used by redeeming_user
    """
    def trans():
        coupon = get_and_validate_news_coupon(coupon_id, service_identity_user,
                                              redeeming_user)
        if not coupon.redeemed_by:
            coupon.redeemed_by = KVStore(coupon.key())
            kv_store = {'users': []}
        else:
            kv_store = coupon.redeemed_by.to_json_dict()

        redeemed_object = {
            'user': redeeming_user.email(),
            'redeemed_on': now()
        }
        kv_store['users'].append(redeemed_object)
        coupon.redeemed_by.from_json_dict(kv_store)
        coupon.put()
        user, app_id = get_app_user_tuple(redeeming_user)
        member = BaseMemberTO(user.email(), app_id)
        news.disable_news(
            coupon.news_id, [member],
            get_identity_from_service_identity_user(service_identity_user))
        return coupon

    return run_in_xg_transaction(trans)
Example #15
0
def cancel_subscription(customer_id, cancel_reason, immediately=False):
    """
    Marks the customer his subscription as disabled.
    Recurrent billing will disable the service and disconnect all users after the subscription has ended.
    When the 'immediately' parameter is set, the service will be disabled immediately.

    Args:
        customer_id (int, long): Customer id
        cancel_reason (unicode): Reason why the subscription has been canceled.
        immediately (bool): Set to True to disable the service immediately

    Returns: None

    Raises:
        EmptyValueException
        CustomerNotFoundException
        NoSubscriptionException
    """
    if not cancel_reason:
        raise EmptyValueException('cancel_reason')

    customer = Customer.get_by_id(customer_id)

    if not customer.subscription_order_number:
        raise NoSubscriptionException(customer)

    order = Order.get_by_order_number(customer_id,
                                      customer.subscription_order_number)
    if immediately or order.status != Order.STATUS_SIGNED:

        def trans():
            try:
                cancel_order(customer, customer.subscription_order_number)
            except OrderAlreadyCanceledException as exception:
                logging.info('Order %s already canceled, continueing...' %
                             exception.order.order_number)

            customer.disabled_reason = cancel_reason
            set_service_disabled(customer, Customer.DISABLED_OTHER)

        run_in_xg_transaction(trans)
    else:
        customer.disabled_reason = cancel_reason
        customer.subscription_cancel_pending_date = now()
        customer.put()
Example #16
0
def _generate_vouchers_qr_codes(service_user, app_id, sln_qr_export_key,
                                voucher_ids):
    tags = list()
    for voucher_id in voucher_ids:
        data = dict(app_id=app_id, voucher_id=voucher_id)
        info = json.dumps(data).decode('utf8')
        tag = POKE_TAG_CITY_VOUCHER_QR + info
        tags.append(tag)

    users.set_user(service_user)
    try:
        qr_details = qr.bulk_create("City Voucher QR Code", tags, None)
    finally:
        users.clear_user()

    def trans():
        voucher_ancestor_key = SolutionCityVoucher.create_parent_key(app_id)
        vouchers = SolutionCityVoucher.get_by_id(voucher_ids,
                                                 voucher_ancestor_key)
        to_put = list()
        for voucher, qr_detail in zip(vouchers, qr_details):
            voucher.image_uri = qr_detail.image_uri
            voucher.content_uri = qr_detail.content_uri
            _set_search_fields(voucher)
            to_put.append(voucher)

            history = SolutionCityVoucherTransaction(parent=voucher)
            history.created = voucher.created
            history.action = SolutionCityVoucherTransaction.ACTION_CREATED
            history.value = 0
            history.service_user = None
            history.service_identity = None
            to_put.append(history)

        sln_qr_export = db.get(sln_qr_export_key)
        sln_qr_export.ready = True
        to_put.append(sln_qr_export)
        put_in_chunks(to_put)
        channel.send_message(
            service_user,
            'solutions.common.city.vouchers.qrcode_export.updated')

    run_in_xg_transaction(trans)
def send_welcome_message(user):
    def trans():
        ui = UserInteraction.get_by_key_name(user.email(), parent=parent_key(user)) or UserInteraction(
            key_name=user.email(), parent=parent_key(user))
        if ui.interactions & UserInteraction.INTERACTION_WELCOME != UserInteraction.INTERACTION_WELCOME:
            ui.interactions |= UserInteraction.INTERACTION_WELCOME
            db.put_async(ui)
            user_profile = get_user_profile(user)

            app, app_translations = get_app_and_translations_by_app_id(user_profile.app_id)
            if app.type in (App.APP_TYPE_ENTERPRISE, App.APP_TYPE_YSAAA, App.APP_TYPE_OSA_LOYALTY):
                return
            msg = app_translations.get_translation(user_profile.language, '_welcome_msg') if app_translations else None
            if not msg:
                msg = localize(user_profile.language, '_welcome_msg', app_name=app.name,
                               contact_email_address=app.get_contact_email_address())
            sendMessage(MC_DASHBOARD, [UserMemberTO(user, Message.ALERT_FLAG_VIBRATE)], Message.FLAG_ALLOW_DISMISS, 0,
                        None, msg, [], None, get_app_by_user(user).core_branding_hash, None, is_mfr=False)

    run_in_xg_transaction(trans)
    def test_user_data(self):
        service_user = self._prepare_svc()

        friend_email = u"*****@*****.**" % time.time()

        data = json.dumps(dict(test="hihihi")).decode('utf8')

        self.assertRaises(FriendNotFoundException, put_user_data, friend_email, data)  # non-existing user

        human_user = users.User(friend_email)
        create_user_profile(human_user, friend_email, language=u'nl')
        self.assertRaises(FriendNotFoundException, put_user_data, friend_email, data)  # user is no friend

        makeFriends(human_user, service_user, None, None, None)

        data = json.dumps(dict(test="ikkel", moe="hahaha")).decode('utf8')
        put_user_data(friend_email, data)

        data = json.dumps(dict(test="tikkel", john="doe")).decode('utf8')
        put_user_data(friend_email, data)

        get_helper = lambda: FriendHelper.from_data_store(service_user, FRIEND_TYPE_SERVICE)
        get_friendto = lambda: run_in_xg_transaction(lambda: FriendTO.fromDBFriendMap(get_helper(),
                                                                                      get_friends_map(human_user),
                                                                                      service_user,
                                                                                      True,
                                                                                      True,
                                                                                      human_user))

        ud = db.get(UserData.createKey(human_user, create_service_identity_user(service_user)))
        ud_dict = ud.userData.to_json_dict()
        ud_dict['__rt__disabledBroadcastTypes'] = ud_dict.get('__rt__disabledBroadcastTypes', [])
        self.assertDictEqual(dict(test="tikkel", moe="hahaha", john="doe", __rt__disabledBroadcastTypes=[]),
                             ud_dict)
        self.assertTrue(get_friendto().hasUserData)

        self.assertRaises(InvalidJsonStringException, put_user_data, friend_email, "invalid user data")
        self.assertRaises(InvalidJsonStringException, put_user_data, friend_email, "")

        del_user_data(friend_email, ["test"])
        self.assertTrue(get_friendto().hasUserData)

        del_user_data(friend_email, ["moe", "john"])
        self.assertFalse(get_friendto().hasUserData)

        japanese = u"スキーに行くのが好きです。"
        data_dict = dict(test="ikkel", moe="hahaha", japanese=japanese)
        data = json.dumps(data_dict).decode('utf8')
        put_user_data(friend_email, data)
        data = json.loads(get_user_data(friend_email, ["japanese"]))
        self.assertEqual(japanese, data["japanese"])

        data = json.loads(get_user_data(friend_email, ["japanese", "test", "moe"]))
        self.assertDictEqual(data, data_dict)
Example #19
0
def _2500_migrate_service_data(job_key, si_keys=None):
    phase = MigrateServiceJob.PHASE_2500_MIGRATE_SERVICE_DATA
    next_phase = MigrateServiceJob.PHASE_3000_REVOKE_ROLES

    # Validate that the job still exists
    job = _get_job(job_key, phase)
    _log_progress(job)

    # Do the work
    if si_keys is None:
        si_keys = list(get_service_identities_query(job.from_service_user, keys_only=True))

    if si_keys:
        si_keys_chunk = list()
        while si_keys and len(si_keys_chunk) < 200:
            si_keys_chunk.append(si_keys.pop(0))

        def trans():
            si_chunk = db.get(si_keys_chunk)
            for si in reversed(si_chunk):
                if not si.serviceData:
                    si_chunk.remove(si)
                    continue
                old_ancestor = si.serviceData._ancestor
                new_ancestor = _create_new_key(job, si.serviceData._ancestor)
                for chunk in chunks(si.serviceData._keys.values(), 5):
                    _migrate_models(job, db.get([KVBucket.create_key(bucket_id, old_ancestor)
                                                 for bucket_id in chunk]))
                for chunk in chunks(si.serviceData._blob_keys.values(), 5):
                    for blob_bucket_ids in chunk:
                        _migrate_models(job, db.get([KVBlobBucket.create_key(blob_bucket_id, old_ancestor)
                                                     for blob_bucket_id in blob_bucket_ids]))
                si.serviceData._ancestor = new_ancestor
            if si_chunk:
                put_and_invalidate_cache(*si_chunk)

            if si_keys:
                # there is still work to do
                deferred.defer(_2500_migrate_service_data, job_key, si_keys,
                               _transactional=True, _queue=MIGRATION_QUEUE)
                return False
            return True

        done = run_in_xg_transaction(trans)
    else:
        done = True

    if done:
        # Set the next phase
        _set_job_in_next_phase(job_key, phase, next_phase)
def _job(customer_key):
    def trans():
        to_put = list()
        customer = Customer.get(customer_key)
        if App.APP_ID_ROGERTHAT not in customer.app_ids and customer.service_email:
            customer.app_ids.append(App.APP_ID_ROGERTHAT)
            to_put.append(customer)
            service_identity = get_default_service_identity(
                customer.service_user)
            if App.APP_ID_ROGERTHAT not in service_identity.appIds:
                service_identity.appIds.append(App.APP_ID_ROGERTHAT)
                deferred.defer(re_index,
                               service_identity.service_identity_user,
                               _queue=MIGRATION_QUEUE,
                               _transactional=True)
                to_put.append(service_identity)
            deferred.defer(re_index_customer,
                           customer.key(),
                           _queue=MIGRATION_QUEUE,
                           _transactional=True)
            put_and_invalidate_cache(*to_put)

    run_in_xg_transaction(trans)
Example #21
0
def discussion_group_deleted(service_user, parent_message_key, member,
                             timestamp, service_identity, tag):
    app_user_email = member.toAppUser().email()

    def trans():
        discussion_group_id = json.loads(tag)['id']
        discussion_group = get_discussion_group(service_user,
                                                discussion_group_id)
        if not discussion_group:
            logging.info('Discussion group %s not found', discussion_group_id)
            return

        kv_store_dict = discussion_group.members.to_json_dict()
        members = kv_store_dict['members']
        if app_user_email not in members:
            logging.info('%s not found in discussion group %s',
                         discussion_group_id)
            return

        members.remove(app_user_email)
        discussion_group.members.from_json_dict(kv_store_dict)
        discussion_group.put()

    run_in_xg_transaction(trans)
Example #22
0
def bulk_invite_result(service_user, service_identity, tag, email, result, user_details):
    if not tag:
        logging.exception("Expected tag in bulk_invite_result")
        return

    if tag in (SERVICE_AUTOCONNECT_INVITE_TAG, APP_BROADCAST_TAG):
        return

    try:
        key = db.Key(tag)
    except db.BadKeyError:
        logging.info('Tag is no db.Key: %s. Ignoring...', tag)
        return

    def trans():
        invite = db.get(reconstruct_key(key))
        if not invite:
            logging.error("Invite object not found in datastore")
            return
        save_message = False
        if "accepted" == result:
            invite.status = RestaurantInvite.STATUS_ACCEPTED
            save_message = True
        else:
            invite.status = RestaurantInvite.STATUS_REJECTED
        invite.put()
        return save_message

    save_message = run_in_xg_transaction(trans)
    if save_message:
        now_ = now()
        sln_settings = get_solution_settings(service_user)
        msg = common_translate(sln_settings.main_language, SOLUTION_COMMON, 'if-accepted-invitation',
                               if_name=user_details[0].name,
                               if_email=user_details[0].email)

        message = create_solution_inbox_message(
            service_user, service_identity, SolutionInboxMessage.CATEGORY_BULK_INVITE, None, False, user_details, now_, msg, False)
        app_user = create_app_user_by_email(user_details[0].email, user_details[0].app_id)
        send_inbox_forwarders_message(service_user, service_identity, app_user, msg, {
            'if_name': user_details[0].name,
            'if_email': user_details[0].email
        }, message_key=message.solution_inbox_message_key, reply_enabled=message.reply_enabled, send_reminder=False)

        sln_i_settings = get_solution_settings_or_identity_settings(sln_settings, service_identity)
        send_message(service_user, u"solutions.common.messaging.update",
                     service_identity=service_identity,
                     message=serialize_complex_value(SolutionInboxMessageTO.fromModel(message, sln_settings, sln_i_settings, True), SolutionInboxMessageTO, False))
Example #23
0
def put_discussion_group(service_user,
                         topic,
                         description,
                         discussion_group_id=None):
    is_new = discussion_group_id is None
    if is_new:
        discussion_group_id = allocate_id(SolutionDiscussionGroup)

        # Create the chat in Rogerthat
        from solutions.common.bizz.messaging import POKE_TAG_DISCUSSION_GROUPS
        tag = json.dumps({
            'id': discussion_group_id,
            '__rt__.tag': POKE_TAG_DISCUSSION_GROUPS
        })
        members = list()
        flags = messaging.ChatFlags.ALLOW_ANSWER_BUTTONS | messaging.ChatFlags.ALLOW_PICTURE
        message_key = messaging.start_chat(members,
                                           topic,
                                           description,
                                           tag=tag,
                                           flags=flags,
                                           default_sticky=True)

    key = SolutionDiscussionGroup.create_key(service_user, discussion_group_id)

    def trans(is_new):
        if is_new:
            # Create the model
            discussion_group = SolutionDiscussionGroup(
                key=key, message_key=message_key, creation_timestamp=now())
            discussion_group.members = KVStore(key)
            discussion_group.members.from_json_dict(dict(members=list()))
        else:
            discussion_group = SolutionDiscussionGroup.get(key)
            if not discussion_group:
                raise HttpNotFoundException()

        discussion_group.topic = topic
        discussion_group.description = description
        discussion_group.put()
        return discussion_group

    return run_in_xg_transaction(trans, is_new)
def set_default_app(app_id):
    def trans(old_default_app_key):
        old_default_app = get_default_app()
        new_default_app = get_app(app_id)
        if new_default_app.key() == old_default_app.key():
            return new_default_app

        new_default_app.is_default = True

        if old_default_app_key:
            old_default_app.is_default = False
            put_and_invalidate_cache(new_default_app, old_default_app)
            on_trans_committed(logging.info, 'Default app updated from %s (%s) to %s (%s)', old_default_app.app_id,
                               old_default_app.name, new_default_app.app_id, new_default_app.name)
        else:
            put_and_invalidate_cache(new_default_app)
        return new_default_app

    app = run_in_xg_transaction(trans)
    invalidate_cache(get_default_app)
    return app
Example #25
0
def set_menu_item_image(service_user, message_flow_run_id, member, steps,
                        end_id, end_message_flow_id, parent_message_key, tag,
                        result_key, flush_id, flush_message_flow_id,
                        service_identity, user_details):

    url = steps[-1].form_result.result.value
    azzert(url)

    tag_dict = json.loads(tag)
    category_name = tag_dict['category_name']
    item_name = tag_dict['item_name']
    category_id = tag_dict['category_id']
    item_id = tag_dict['item_id']

    def create_error(message):
        result = MessageCallbackResultTypeTO()
        result.message = message
        return result

    download_image = partial(download, url)

    def trans():
        sln_settings = get_solution_settings(service_user)
        menu = get_restaurant_menu(service_user, sln_settings.solution)
        category = menu.categories[category_id]
        if not category:
            return create_error(
                common_translate(sln_settings.main_language,
                                 SOLUTION_COMMON,
                                 u'category_not_found',
                                 name=category_name))
        for item in category.items:
            if item.id == item_id:
                break
        else:
            return create_error(
                common_translate(sln_settings.main_language,
                                 SOLUTION_COMMON,
                                 u'item_not_found',
                                 name=item_name))

        if item.image_id:
            delete_file_blob(service_user, item.image_id)

        response = download_image()
        if response.status_code != 200:
            return create_error(
                common_translate(sln_settings.main_language, SOLUTION_COMMON,
                                 u'error-occured-unknown-try-again'))

        item.image_id = create_file_blob(service_user,
                                         response.content).key().id()
        menu.put()
        on_trans_committed(channel.send_message,
                           service_user,
                           'solutions.common.menu.item_image_configured',
                           category=serialize_complex_value(
                               category, MenuCategory, False),
                           item=serialize_complex_value(item, MenuItem, False))
        return None

    return run_in_xg_transaction(trans)
def update_app(app_id, data):
    """
    Args:
        app_id (unicode)
        data(AppTO)
    Returns:
        App
    """
    # Validation
    if data.user_regex:
        validate_user_regex(data.user_regex)

    # checking this non-transactional to prevent accessing too many entity groups in the transaction
    auto_connected_identities = db.get([ServiceIdentity.keyFromUser(add_slash_default(users.User(acs.service_identity_email)))
                                        for acs in data.auto_connected_services])
    identities_to_patch = []
    for si in auto_connected_identities:
        if not si:
            raise ServiceIdentityDoesNotExistException(si.user.email())
        if app_id not in si.appIds:
            identities_to_patch.append(si.user)

    def trans():
        app = get_app(app_id)
        to_put = [app]

        app.admin_services = data.admin_services
        app.name = data.name
        app.type = data.type
        app.main_service = data.main_service
        app.facebook_app_id = data.facebook_app_id
        app.ios_app_id = data.facebook_app_secret
        app.android_app_id = data.android_app_id
        app.dashboard_email_address = data.dashboard_email_address
        app.contact_email_address = data.contact_email_address
        app.user_regex = data.user_regex
        app.demo = data.demo
        app.beta = data.beta
        app.secure = data.secure
        app.chat_enabled = data.chat_enabled
        app.mdp_client_id = data.mdp_client_id
        app.mdp_client_secret = data.mdp_client_secret
        app.owncloud_base_uri = data.owncloud_base_uri
        app.owncloud_admin_username = data.owncloud_admin_username
        app.owncloud_admin_password = data.owncloud_admin_password

        if set(app.embedded_apps).symmetric_difference(data.embedded_apps):
            deferred.defer(send_update_all_embedded_apps, app_id, _countdown=2, _transactional=True)
        app.embedded_apps = data.embedded_apps

        old_auto_connected_services = {acs.service_identity_email for acs in app.auto_connected_services}
        app.auto_connected_services = AutoConnectedServices()
        for acs in data.auto_connected_services:
            service_identity_user = add_slash_default(users.User(acs.service_identity_email))
            if service_identity_user in identities_to_patch:
                si = get_service_identity(service_identity_user)
                si.appIds.append(app_id)
                to_put.append(si)
            acs.service_identity_email = service_identity_user.email()
            app.auto_connected_services.add(acs)

        new_acs = [acs for acs in app.auto_connected_services
                   if acs.service_identity_email not in old_auto_connected_services]
        if new_acs:
            logging.info('There are new auto-connected services for %s: %s', app_id,
                         [acs.service_identity_email for acs in new_acs])
        for acs in new_acs:
            deferred.defer(connect_auto_connected_service, app_id, acs, _transactional=True)
        put_and_invalidate_cache(*to_put)
        return app

    return run_in_xg_transaction(trans)
Example #27
0
def _create_charge(order_key, today, products):
    def cleanup_expired_subscription(customer):
        expired_subscription = ExpiredSubscription.get_by_customer_id(
            customer.id)
        if expired_subscription:
            logging.info(
                'Cleaning up ExpiredSubscription from customer %s because he has linked his credit card',
                customer.name)
            expired_subscription.delete()

    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

    try:
        return run_in_xg_transaction(trans)
    except Exception as e:
        logging.exception("Failed to create new charge: %s" % e.message,
                          _suppress=False)
Example #28
0
def put_news_item(service_identity_user,
                  title,
                  message,
                  broadcast_type,
                  sponsored,
                  image,
                  action_button,
                  order_items,
                  news_type,
                  qr_code_caption,
                  app_ids,
                  scheduled_at,
                  news_id=None,
                  broadcast_on_facebook=False,
                  broadcast_on_twitter=False,
                  facebook_access_token=None,
                  target_audience=None,
                  role_ids=None,
                  host=None):
    """
    Creates a news item first then processes the payment if necessary (not necessary for non-promoted posts).
    If the payment was unsuccessful it will be retried in a deferred task.

    Args:
        service_identity_user (users.User)
        title (unicode)
        message (unicode)
        broadcast_type (unicode)
        sponsored (bool)
        image (unicode)
        action_button (NewsActionButtonTO)
        order_items (list of OrderItemTO)
        news_type (int)
        qr_code_caption (unicode)
        app_ids (list of unicode)
        scheduled_at (long)
        news_id (long): id of the news item to update. When not provided a new news item will be created.
        broadcast_on_facebook (bool)
        broadcast_on_twitter (bool)
        facebook_access_token (unicode): user or page access token
        target_audience (NewsTargetAudienceTO)
        role_ids (list of long) the list of role ids to filter sending the news to their members
        host (unicode): host of the api request (used for social media apps)

    Returns:
        news_item (NewsBroadcastItemTO)
    """
    if not order_items or order_items is MISSING:
        order_items = []
    if news_type == NewsItem.TYPE_QR_CODE:
        sln_settings = get_solution_settings(
            get_service_user_from_service_identity_user(service_identity_user))
        azzert(SolutionModule.LOYALTY in sln_settings.modules)
    sponsored_until = None
    should_save_coupon = news_type == NewsItem.TYPE_QR_CODE and not news_id
    sponsored_app_ids = set()
    extra_app_ids = []
    si = get_service_identity(service_identity_user)
    for order_item in reversed(order_items):
        if order_item.product == Product.PRODUCT_NEWS_PROMOTION and sponsored:
            azzert(order_item.app_id)
            azzert(order_item.app_id not in sponsored_app_ids)
            sponsored_app_ids.add(order_item.app_id)
            order_item.count = get_sponsored_news_count_in_app(
                service_identity_user, order_item.app_id).count
        elif order_item.product == Product.PRODUCT_EXTRA_CITY:
            azzert(order_item.app_id)
            azzert(order_item.app_id not in extra_app_ids)
            extra_app_ids.append(order_item.app_id)
            if order_item.app_id in si.appIds:
                order_items.remove(order_item)
        else:
            raise BusinessException('Invalid product %s' % order_item.product)

    if not news_id and not app_ids:
        raise BusinessException(
            'Please select at least one app to publish this news in')
    if sponsored:
        sponsored_until_date = datetime.datetime.utcnow() + datetime.timedelta(
            days=SPONSOR_DAYS)
        sponsored_until = long(sponsored_until_date.strftime('%s'))
        # for sponsored news that is free in certain apps no order item is given, so add it here
        sponsored_counts = get_sponsored_news_count(service_identity_user,
                                                    app_ids)
        for sponsored_count in sponsored_counts:
            if sponsored_count.remaining_free != 0 and sponsored_count.app_id in app_ids:
                sponsored_app_ids.add(sponsored_count.app_id)
        app_ids = list(sponsored_app_ids)

    service_user, identity = get_service_identity_tuple(service_identity_user)
    default_app = get_app(si.defaultAppId)
    if App.APP_ID_ROGERTHAT in si.appIds and App.APP_ID_ROGERTHAT not in app_ids:
        app_ids.append(App.APP_ID_ROGERTHAT)
    if default_app.demo and App.APP_ID_ROGERTHAT in app_ids:
        app_ids.remove(App.APP_ID_ROGERTHAT)
    kwargs = {
        'sticky': sponsored,
        'sticky_until': sponsored_until,
        'message': message,
        'broadcast_type': broadcast_type,
        'service_identity': identity,
        'news_id': news_id,
        'app_ids': app_ids,
        'image': image,
        'scheduled_at': scheduled_at,
        'target_audience': target_audience,
        'role_ids': role_ids
    }
    if not news_id:
        kwargs['news_type'] = news_type

    if news_type == NewsItem.TYPE_QR_CODE:
        if should_save_coupon:

            def trans():
                coupon = NewsCoupon(
                    parent=NewsCoupon.create_parent_key(service_identity_user),
                    content=qr_code_caption)
                coupon.put()
                return coupon

            coupon = db.run_in_transaction(trans)
            kwargs['qr_code_content'] = u'%s' % json.dumps({'c': coupon.id})
        kwargs['qr_code_caption'] = qr_code_caption
    elif news_type == NewsItem.TYPE_NORMAL:
        kwargs.update({
            'action_buttons': [action_button] if action_button else [],
            'title':
            title
        })
    else:
        raise BusinessException('Invalid news type')
    for key, value in kwargs.items():
        if value is MISSING:
            del kwargs[key]

    with users.set_user(service_user):
        try:

            def trans():
                news_item = news.publish(accept_missing=True, **kwargs)
                if should_save_coupon:
                    _save_coupon_news_id(news_item.id, coupon)
                elif news_type == NewsItem.TYPE_QR_CODE and qr_code_caption is not MISSING and qr_code_caption and news_id:
                    news_coupon = NewsCoupon.get_by_news_id(
                        service_identity_user, news_id)
                    if news_coupon:
                        news_coupon.content = qr_code_caption
                        news_coupon.put()
                    else:
                        logging.warn(
                            'Not updating qr_code_caption for non-existing coupon for news with id %d',
                            news_id)
                if order_items:
                    create_and_pay_news_order(service_user, news_item.id,
                                              order_items)
                return news_item

            news_item = run_in_xg_transaction(trans)
            if broadcast_on_facebook or broadcast_on_twitter:
                if scheduled_at is not MISSING and scheduled_at > 0:
                    schedule_post_to_social_media(service_user, host,
                                                  broadcast_on_facebook,
                                                  broadcast_on_twitter,
                                                  facebook_access_token,
                                                  news_item.id, scheduled_at)
                else:
                    post_to_social_media(service_user, broadcast_on_facebook,
                                         broadcast_on_twitter,
                                         facebook_access_token, news_item.id)

            return NewsBroadcastItemTO.from_news_item_to(
                news_item, broadcast_on_facebook, broadcast_on_twitter)
        except:
            if should_save_coupon:
                db.delete_async(coupon)
            raise
def set_expired_subscription_status(customer_id, status):
    to_put = list()
    to_delete = list()
    current_user = gusers.get_current_user()
    if not is_admin(current_user):
        raise NoPermissionException('set expired subscription status')
    if not status in ExpiredSubscription.STATUSES or status == ExpiredSubscription.STATUS_EXPIRED:
        raise BusinessException('Invalid expired subscription status (%d)', status)

    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

    return run_in_xg_transaction(trans)
def _heart_beat(current_user, current_mobile, majorVersion, minorVersion, flushBackLog, appType, product, timestamp,
                timezone, timezoneDeltaGMT, osVersion, deviceModelName, simCountry, simCountryCode, simCarrierName,
                simCarrierCode, netCountry, netCountryCode, netCarrierName, netCarrierCode, localeLanguage,
                localeCountry, now_time, embeddedApps, deviceId):
    from rogerthat.bizz.look_and_feel import update_look_and_feel_for_user
    m = current_mobile
    mobile_key = m.key()
    ms_key = get_mobile_settings_cached(m).key()

    embeddedApps = MISSING.default(embeddedApps, [])

    def trans():
        # type: () -> tuple[Mobile, UserProfile, bool]
        keys = (mobile_key, ms_key, get_user_profile_key(current_user))
        mobile, ms, my_profile = db.get(keys)  # type: (Mobile, MobileSettings, UserProfile)
        if mobile.account not in my_profile.mobiles:
            logging.warn('Mobile account "%s" of user %s has been unregistered', mobile.account, current_user)
            return mobile, my_profile, False

        if appType != MISSING:
            mobile.type = my_profile.mobiles[mobile.account].type_ = appType
        if simCountry != MISSING:
            mobile.simCountry = simCountry
        if simCountryCode != MISSING:
            mobile.simCountryCode = simCountryCode
        if simCarrierCode != MISSING:
            mobile.simCarrierCode = simCarrierCode
        if simCarrierName != MISSING:
            mobile.simCarrierName = simCarrierName
        if netCountry != MISSING:
            mobile.netCountry = netCountry
        if netCountryCode != MISSING:
            mobile.netCountryCode = netCountryCode
        if netCarrierCode != MISSING:
            mobile.netCarrierCode = netCarrierCode
        if netCarrierName != MISSING:
            mobile.netCarrierName = netCarrierName
        if deviceModelName != MISSING:
            mobile.hardwareModel = deviceModelName
        if osVersion != MISSING:
            mobile.osVersion = osVersion
        if localeCountry != MISSING:
            mobile.localeCountry = localeCountry
        if localeLanguage != MISSING:
            mobile.localeLanguage = localeLanguage
        if timezone != MISSING:
            mobile.timezone = timezone
        if timezoneDeltaGMT != MISSING:
            mobile.timezoneDeltaGMT = timezoneDeltaGMT
        if deviceId != MISSING:
            mobile.deviceId = deviceId

        language = mobile.localeLanguage
        if language:
            should_update_embedded_apps = False
            if '-' in language:
                language = get_iso_lang(language.lower())
            elif mobile.localeCountry:
                language = '%s_%s' % (mobile.localeLanguage, mobile.localeCountry)

            if my_profile.language != language:
                my_profile.language = language
                # trigger friend.update service api call
                deferred.defer(update_friend_service_identity_connections, my_profile.key(), [u"language"],
                               _transactional=True)
                db.delete_async(get_broadcast_settings_flow_cache_keys_of_user(my_profile.user))
                if embeddedApps:
                    should_update_embedded_apps = True
            deferred.defer(update_look_and_feel_for_user, current_user, _transactional=True, _queue=FAST_QUEUE)

            # User updated to app version x.1.x, send custom translations
            # todo: this will also trigger when user updates from 3.0.x to 3.1.x which we don't really want
            should_update_embedded_apps = should_update_embedded_apps or majorVersion > 1 and ms.majorVersion == 0
            # Update when embedded apps are different
            should_update_embedded_apps = should_update_embedded_apps or not set(embeddedApps).issubset(
                set(my_profile.embedded_apps or []))
            if should_update_embedded_apps:
                deferred.defer(update_embedded_app_translations_for_user, current_user, embeddedApps, language,
                               _transactional=True)

        ms.majorVersion = majorVersion
        ms.minorVersion = minorVersion
        ms.lastHeartBeat = now_time

        my_profile.country = mobile.netCountry or mobile.simCountry or mobile.localeCountry
        my_profile.timezone = mobile.timezone
        my_profile.timezoneDeltaGMT = mobile.timezoneDeltaGMT
        my_profile.embedded_apps = embeddedApps
        must_update_app_settings = False
        if my_profile.tos_version != get_current_document_version(DOC_TERMS):
            if mobile.is_android:
                version = Features.ASK_TOS.android
            elif mobile.is_ios:
                version = Features.ASK_TOS.ios
            else:
                version = Version(0, 1)
            must_update_app_settings = Version(majorVersion, minorVersion) >= version
        put_and_invalidate_cache(ms, mobile, my_profile)
        return mobile, my_profile, must_update_app_settings
    mobile, profile, must_update_app_settings = run_in_xg_transaction(trans)
    if must_update_app_settings:
        from rogerthat.bizz.app import push_app_settings_to_user
        # This will ask to agree to Terms and Conditions when version has changed
        push_app_settings_to_user(profile, mobile.app_id)
Example #31
0
def create_reseller_invoice_for_legal_entity(legal_entity,
                                             start_date,
                                             end_date,
                                             do_send_email=True):
    """
    Args:
        legal_entity (LegalEntity) 
        start_date (long)
        end_date (long)
        do_send_email (bool)
    """
    if legal_entity.is_mobicage:
        # To avoid a composite index we don't filter on is_mobicage
        return
    solution_server_settings = get_solution_server_settings()
    from_email = solution_server_settings.shop_no_reply_email
    to_emails = solution_server_settings.shop_payment_admin_emails
    mobicage_legal_entity = get_mobicage_legal_entity()
    logging.info(
        'Exporting reseller invoices for legal entity %s(id %d) from %s(%s) to %s(%s)',
        legal_entity.name, legal_entity.id, start_date, time.ctime(start_date),
        end_date, time.ctime(end_date))
    invoices = list(Invoice.all().filter(
        'legal_entity_id',
        legal_entity.id).filter('paid_timestamp >', start_date).filter(
            'paid_timestamp <', end_date).filter('paid', True).filter(
                'payment_type IN',
                (Invoice.PAYMENT_MANUAL, Invoice.PAYMENT_MANUAL_AFTER)))
    start_time = time.strftime('%m/%d/%Y', time.gmtime(int(start_date)))
    end_time = time.strftime('%m/%d/%Y', time.gmtime(int(end_date)))
    if not invoices:
        message = 'No new invoices for reseller %s for period %s - %s' % (
            legal_entity.name, start_time, end_time)
        logging.info(message)
        if do_send_email:
            send_mail(from_email, to_emails, message, message)
        return
    items_per_customer = {}
    customers_to_get = set()
    products = {
        p.code: p
        for p in Product.list_by_legal_entity(legal_entity.id)
    }
    for invoice in invoices:
        # get all subscription order items
        order_items = list(OrderItem.list_by_order(invoice.order_key))
        for item in reversed(order_items):
            product = products[item.product_code]
            # We're only interested in subscription items
            if product.is_subscription or product.is_subscription_extension or product.is_subscription_discount:
                if invoice.customer_id not in items_per_customer:
                    items_per_customer[invoice.customer_id] = []
                    customers_to_get.add(
                        Customer.create_key(invoice.customer_id))
                items_per_customer[invoice.customer_id].append(item)
            else:
                order_items.remove(item)
    if not customers_to_get:
        message = 'No new invoices containing subscriptions for reseller %s for period %s - %s' % (
            legal_entity.name, start_time, end_time)
        logging.info(message)
        if do_send_email:
            send_mail(from_email, to_emails, message, message)
        return
    customers = {c.id: c for c in db.get(customers_to_get)}
    product_totals = {}
    for customer_id in items_per_customer:
        items = items_per_customer[customer_id]
        for item in items:
            if item.product_code not in product_totals:
                product_totals[item.product_code] = {
                    'count': 0,
                    'price': int(item.price * legal_entity.revenue_percent)
                }
            product_totals[item.product_code]['count'] += item.count
    total_amount = 0
    for product in product_totals:
        p = product_totals[product]
        price = p['count'] * p['price']
        p['total_price'] = format_currency(
            price / 100.,
            legal_entity.currency_code,
            locale=mobicage_legal_entity.country_code)
        total_amount += price
    total_amount_formatted = format_currency(
        total_amount / 100.,
        legal_entity.currency_code,
        locale=mobicage_legal_entity.country_code)
    vat_amount = total_amount / mobicage_legal_entity.vat_percent if mobicage_legal_entity.country_code == legal_entity.country_code else 0
    vat_amount_formatted = format_currency(
        vat_amount / 100.,
        legal_entity.currency_code,
        locale=mobicage_legal_entity.country_code)
    from_date = format_datetime(datetime.utcfromtimestamp(start_date),
                                locale=SHOP_DEFAULT_LANGUAGE,
                                format='dd/MM/yyyy HH:mm')
    until_date = format_datetime(datetime.utcfromtimestamp(end_date),
                                 locale=SHOP_DEFAULT_LANGUAGE,
                                 format='dd/MM/yyyy HH:mm')

    solution_server_settings = get_solution_server_settings()
    template_variables = {
        'products':
        products,
        'customers':
        customers,
        'invoices':
        invoices,
        'items_per_customer':
        items_per_customer,
        'product_totals':
        product_totals.items(),
        'mobicage_legal_entity':
        mobicage_legal_entity,
        'legal_entity':
        legal_entity,
        'language':
        SHOP_DEFAULT_LANGUAGE,
        'from_date':
        from_date,
        'until_date':
        until_date,
        'revenue_percent':
        legal_entity.revenue_percent,
        'vat_amount_formatted':
        vat_amount_formatted,
        'total_amount_formatted':
        total_amount_formatted,
        'logo_path':
        '../html/img/osa_white_en_250.jpg',
        'tos_link':
        '<a href="%s">%s</a>' %
        (solution_server_settings.shop_privacy_policy_url,
         solution_server_settings.shop_privacy_policy_url)
    }
    source_html = SHOP_JINJA_ENVIRONMENT.get_template(
        'invoice/reseller_invoice.html').render(template_variables)
    output_stream = StringIO()
    pisa.CreatePDF(src=source_html,
                   dest=output_stream,
                   path='%s/invoice' % SHOP_TEMPLATES_FOLDER)
    invoice_pdf_contents = output_stream.getvalue()
    output_stream.close()
    # Create an order, order items, charge and invoice.
    _now = now()
    customer = legal_entity.get_or_create_customer()
    mobicage_team = RegioManagerTeam.get_mobicage()

    def trans():
        to_put = list()
        order_number = OrderNumber.next(mobicage_legal_entity)
        order_key = db.Key.from_path(Order.kind(),
                                     order_number,
                                     parent=customer.key())
        order = Order(key=order_key)
        order.contact_id = legal_entity.contact_id
        order.date = _now
        order.vat_pct = mobicage_legal_entity.vat_percent if legal_entity.country_code == mobicage_legal_entity.country_code else 0
        order.amount = int(round(total_amount))
        order.vat = int(round(vat_amount))
        order.total_amount = int(round(total_amount + vat_amount))
        order.is_subscription_order = False
        order.is_subscription_extension_order = False
        order.team_id = mobicage_team.id
        order.manager = customer.manager
        order.status = Order.STATUS_SIGNED
        to_put.append(order)

        for i, (product_code, item) in enumerate(product_totals.iteritems()):
            order_item = OrderItem(parent=order_key)
            order_item.number = i + 1
            order_item.comment = products[product_code].default_comment(
                SHOP_DEFAULT_LANGUAGE)
            order_item.product_code = product_code
            order_item.count = item['count']
            order_item.price = item['price']
            to_put.append(order_item)

        charge_key = Charge.create_key(allocate_id(Charge), order_number,
                                       customer.id)
        charge = Charge(key=charge_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.charge_number = ChargeNumber.next(mobicage_legal_entity)
        charge.currency_code = legal_entity.currency_code
        to_put.append(charge)

        invoice_number = InvoiceNumber.next(mobicage_legal_entity)
        invoice = Invoice(key_name=invoice_number,
                          parent=charge,
                          amount=charge.amount,
                          vat_pct=charge.vat_pct,
                          vat=charge.vat,
                          total_amount=charge.total_amount,
                          currency_code=legal_entity.currency_code,
                          date=_now,
                          payment_type=Invoice.PAYMENT_MANUAL_AFTER,
                          operator=charge.manager,
                          paid=False,
                          legal_entity_id=mobicage_legal_entity.id,
                          pdf=invoice_pdf_contents)
        charge.invoice_number = invoice_number
        to_put.append(invoice)
        put_and_invalidate_cache(*to_put)
        return order, charge, invoice

    order, charge, invoice = run_in_xg_transaction(trans)

    if do_send_email:
        serving_url = '%s/internal/shop/invoice/pdf?customer_id=%d&order_number=%s&charge_id=%d&invoice_number=%s' % (
            get_server_settings().baseUrl, customer.id, order.order_number,
            charge.id, invoice.invoice_number)
        subject = 'New reseller invoice for %s, %s - %s' % (
            legal_entity.name, start_time, end_time)
        body_text = 'A new invoice is available for reseller %s for period %s to %s here: %s' % (
            legal_entity.name, start_time, end_time, serving_url)

        send_mail(from_email, to_emails, subject, body_text)
Example #32
0
def add_item_to_order(item):
    service_user = users.get_current_user()
    customer = get_customer(service_user)
    azzert(customer)
    contact = Contact.get_one(customer)
    azzert(contact)
    sln_settings = get_solution_settings(service_user)
    lang = sln_settings.main_language

    # Check if the customer his service isn't already enabled in this city
    if item.app_id in customer.app_ids:
        raise BusinessException(
            translate(lang, SOLUTION_COMMON, u'service_already_active'))

    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

    try:
        order_item, service_visible_in_translation = run_in_xg_transaction(
            trans)
        return OrderItemReturnStatusTO.create(True, None, order_item,
                                              service_visible_in_translation)
    except BusinessException, exception:
        return OrderItemReturnStatusTO.create(False, exception.message, None)
Example #33
0
def follow_discussion_groups(service_user, status, form_result, answer_id,
                             member, message_key, tag, received_timestamp,
                             acked_timestamp, parent_message_key, result_key,
                             service_identity, user_details):
    if answer_id != FormTO.POSITIVE or form_result in (
            None, MISSING) or form_result.result in (None, MISSING):
        return None

    app_user = user_details[0].toAppUser()
    app_user_email = app_user.email()
    selected_ids = map(long, form_result.result.values)

    def rm_from_chat(parent_message_key):
        messaging.delete_chat_members(parent_message_key,
                                      [MemberTO.from_user(app_user)])

    def add_to_chat(parent_message_key):
        messaging.add_chat_members(parent_message_key,
                                   [MemberTO.from_user(app_user)])

    def trans():
        followed_new_group = False
        to_put = list()
        for discussion_group in SolutionDiscussionGroup.list(service_user):
            kv_store_dict = discussion_group.members.to_json_dict()
            was_following = app_user_email in kv_store_dict['members']
            now_following = discussion_group.id in selected_ids

            if was_following != now_following:
                if now_following:
                    followed_new_group = True
                    logging.debug('Adding %s to discussion group "%s"',
                                  app_user_email, discussion_group.topic)
                    kv_store_dict['members'].append(app_user_email)
                    on_trans_committed(add_to_chat,
                                       discussion_group.message_key)
                else:
                    logging.debug('Removing %s from discussion group "%s"',
                                  app_user_email, discussion_group.topic)
                    kv_store_dict['members'].remove(app_user_email)
                    on_trans_committed(rm_from_chat,
                                       discussion_group.message_key)
                to_put.append(discussion_group)
                # We need to set members again to force the KVStore to be put
                discussion_group.members.from_json_dict(kv_store_dict)
        if to_put:
            put_and_invalidate_cache(*to_put)
        return followed_new_group

    followed_new_group = run_in_xg_transaction(trans)

    sln_settings, sln_main_branding = db.get([
        SolutionSettings.create_key(service_user),
        SolutionMainBranding.create_key(service_user)
    ])

    result = FormAcknowledgedCallbackResultTO()
    result.type = TYPE_MESSAGE
    result.value = MessageCallbackResultTypeTO()
    result.value.alert_flags = Message.ALERT_FLAG_VIBRATE
    result.value.answers = list()
    result.value.attachments = list()
    result.value.branding = sln_main_branding.branding_key
    result.value.dismiss_button_ui_flags = 0
    result.value.flags = Message.FLAG_AUTO_LOCK | Message.FLAG_ALLOW_DISMISS
    result.value.message = translate(sln_settings.main_language,
                                     SOLUTION_COMMON,
                                     u'Your changes have been saved.')
    if followed_new_group:
        result.value.message += u'\n\n%s' % translate(
            sln_settings.main_language, SOLUTION_COMMON,
            u'you_can_find_discussion_groups_on_homescreen')
    result.value.step_id = None
    result.value.tag = None
    return result