<s> import datetime <EOL> import json <EOL> import urllib2 <EOL> from StringIO import StringIO <EOL> from urllib import urlencode <EOL> from django . conf import settings <EOL> from django . db import transaction <EOL> from django . db . models import Q <EOL> from django . http import HttpRequest , QueryDict <EOL> from django . template . loader import render_to_string <EOL> from django . utils . translation import ugettext <EOL> from celery . schedules import crontab <EOL> from celery . task import periodic_task , task <EOL> from couchdbkit import ResourceNotFound <EOL> from couchexport . export import export_from_tables <EOL> from couchexport . models import Format <EOL> from dimagi . utils . couch . database import iter_docs <EOL> from dimagi . utils . django . email import send_HTML_email <EOL> from corehq . apps . accounting import utils <EOL> from corehq . apps . accounting . exceptions import ( <EOL> CreditLineError , <EOL> InvoiceAlreadyCreatedError , <EOL> InvoiceError , <EOL> ) <EOL> from corehq . apps . accounting . invoicing import DomainInvoiceFactory <EOL> from corehq . apps . accounting . models import ( <EOL> BillingAccount , <EOL> Currency , <EOL> Invoice , <EOL> StripePaymentMethod , <EOL> Subscription , <EOL> SubscriptionAdjustment , <EOL> SubscriptionAdjustmentMethod , <EOL> SubscriptionAdjustmentReason , <EOL> WirePrepaymentBillingRecord , <EOL> WirePrepaymentInvoice , <EOL> ) <EOL> from corehq . apps . accounting . payment_handlers import AutoPayInvoicePaymentHandler <EOL> from corehq . apps . accounting . utils import ( <EOL> fmt_dollar_amount , <EOL> get_change_status , <EOL> get_dimagi_from_email_by_product , <EOL> has_subscription_already_ended , <EOL> log_accounting_error , <EOL> log_accounting_info , <EOL> ) <EOL> from corehq . apps . domain . models import Domain <EOL> from corehq . apps . users . models import FakeUser , WebUser <EOL> from corehq . const import USER_DATE_FORMAT , USER_MONTH_FORMAT <EOL> from corehq . util . view_utils import absolute_reverse <EOL> from corehq . util . dates import get_previous_month_date_range <EOL> @ transaction . atomic <EOL> def _activate_subscription ( subscription ) : <EOL> subscription . is_active = True <EOL> subscription . save ( ) <EOL> upgraded_privs = get_change_status ( None , subscription . plan_version ) . upgraded_privs <EOL> subscription . subscriber . activate_subscription ( <EOL> upgraded_privileges = upgraded_privs , <EOL> subscription = subscription , <EOL> ) <EOL> def activate_subscriptions ( based_on_date = None ) : <EOL> """<STR_LIT>""" <EOL> starting_date = based_on_date or datetime . date . today ( ) <EOL> starting_subscriptions = Subscription . objects . filter ( <EOL> date_start = starting_date , <EOL> is_active = False , <EOL> ) <EOL> starting_subscriptions = filter ( <EOL> lambda subscription : not has_subscription_already_ended ( subscription ) , <EOL> starting_subscriptions <EOL> ) <EOL> for subscription in starting_subscriptions : <EOL> try : <EOL> _activate_subscription ( subscription ) <EOL> except Exception as e : <EOL> log_accounting_error ( e . message ) <EOL> @ transaction . atomic <EOL> def _deactivate_subscription ( subscription , ending_date ) : <EOL> subscription . is_active = False <EOL> subscription . save ( ) <EOL> next_subscription = subscription . next_subscription <EOL> activate_next_subscription = next_subscription and next_subscription . date_start == ending_date <EOL> if activate_next_subscription : <EOL> new_plan_version = next_subscription . plan_version <EOL> next_subscription . is_active = True <EOL> next_subscription . save ( ) <EOL> else : <EOL> new_plan_version = None <EOL> _ , downgraded_privs , upgraded_privs = get_change_status ( subscription . plan_version , new_plan_version ) <EOL> if next_subscription and subscription . account == next_subscription . account : <EOL> subscription . transfer_credits ( subscription = next_subscription ) <EOL> else : <EOL> subscription . transfer_credits ( ) <EOL> subscription . subscriber . deactivate_subscription ( <EOL> downgraded_privileges = downgraded_privs , <EOL> upgraded_privileges = upgraded_privs , <EOL> old_subscription = subscription , <EOL> new_subscription = next_subscription if activate_next_subscription else None , <EOL> ) <EOL> def deactivate_subscriptions ( based_on_date = None ) : <EOL> """<STR_LIT>""" <EOL> ending_date = based_on_date or datetime . date . today ( ) <EOL> ending_subscriptions = Subscription . objects . filter ( <EOL> date_end = ending_date , <EOL> is_active = True , <EOL> ) <EOL> for subscription in ending_subscriptions : <EOL> try : <EOL> _deactivate_subscription ( subscription , ending_date ) <EOL> except Exception as e : <EOL> log_accounting_error ( e . message ) <EOL> def warn_subscriptions_still_active ( based_on_date = None ) : <EOL> ending_date = based_on_date or datetime . date . today ( ) <EOL> subscriptions_still_active = Subscription . objects . filter ( <EOL> date_end__lte = ending_date , <EOL> is_active = True , <EOL> ) <EOL> for subscription in subscriptions_still_active : <EOL> log_accounting_error ( "<STR_LIT>" % subscription ) <EOL> def warn_subscriptions_not_active ( based_on_date = None ) : <EOL> based_on_date = based_on_date or datetime . date . today ( ) <EOL> subscriptions_not_active = Subscription . objects . filter ( <EOL> Q ( date_end = None ) | Q ( date_end__gt = based_on_date ) , <EOL> date_start__lte = based_on_date , <EOL> is_active = False , <EOL> ) <EOL> for subscription in subscriptions_not_active : <EOL> log_accounting_error ( "<STR_LIT>" % subscription ) <EOL> @ periodic_task ( run_every = crontab ( minute = <NUM_LIT:0> , hour = <NUM_LIT:5> ) ) <EOL> def update_subscriptions ( ) : <EOL> deactivate_subscriptions ( ) <EOL> activate_subscriptions ( ) <EOL> warn_subscriptions_still_active ( ) <EOL> warn_subscriptions_not_active ( ) <EOL> @ periodic_task ( run_every = crontab ( hour = <NUM_LIT> , minute = <NUM_LIT:0> , day_of_month = '<STR_LIT:1>' ) ) <EOL> def generate_invoices ( based_on_date = None ) : <EOL> """<STR_LIT>""" <EOL> today = based_on_date or datetime . date . today ( ) <EOL> invoice_start , invoice_end = get_previous_month_date_range ( today ) <EOL> log_accounting_info ( "<STR_LIT>" % { <EOL> '<STR_LIT:start>' : invoice_start . strftime ( USER_DATE_FORMAT ) , <EOL> '<STR_LIT:end>' : invoice_end . strftime ( USER_DATE_FORMAT ) , <EOL> } ) <EOL> all_domain_ids = [ d [ '<STR_LIT:id>' ] for d in Domain . get_all ( include_docs = False ) ] <EOL> for domain_doc in iter_docs ( Domain . get_db ( ) , all_domain_ids ) : <EOL> domain = Domain . wrap ( domain_doc ) <EOL> try : <EOL> invoice_factory = DomainInvoiceFactory ( <EOL> invoice_start , invoice_end , domain ) <EOL> invoice_factory . create_invoices ( ) <EOL> log_accounting_info ( "<STR_LIT>" % domain . name ) <EOL> except CreditLineError as e : <EOL> log_accounting_error ( <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" % ( domain . name , e ) <EOL> ) <EOL> except InvoiceError as e : <EOL> log_accounting_error ( <EOL> "<STR_LIT>" % ( domain . name , e ) <EOL> ) <EOL> except InvoiceAlreadyCreatedError as e : <EOL> log_accounting_error ( <EOL> "<STR_LIT>" % ( domain . name , e ) <EOL> ) <EOL> except Exception as e : <EOL> log_accounting_error ( <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" % ( domain . name , e ) <EOL> ) <EOL> def send_bookkeeper_email ( month = None , year = None , emails = None ) : <EOL> today = datetime . date . today ( ) <EOL> today = get_previous_month_date_range ( today ) [ <NUM_LIT:0> ] <EOL> month = month or today . month <EOL> year = year or today . year <EOL> from corehq . apps . accounting . interface import InvoiceInterface <EOL> request = HttpRequest ( ) <EOL> params = urlencode ( ( <EOL> ( '<STR_LIT>' , '<STR_LIT>' ) , <EOL> ( '<STR_LIT>' , month ) , <EOL> ( '<STR_LIT>' , year ) , <EOL> ) ) <EOL> request . GET = QueryDict ( params ) <EOL> request . couch_user = FakeUser ( <EOL> domain = "<STR_LIT>" , <EOL> username = "<STR_LIT>" , <EOL> ) <EOL> invoice = InvoiceInterface ( request ) <EOL> invoice . is_rendered_as_email = True <EOL> first_of_month = datetime . date ( year , month , <NUM_LIT:1> ) <EOL> email_context = { <EOL> '<STR_LIT>' : first_of_month . strftime ( "<STR_LIT>" ) , <EOL> } <EOL> email_content = render_to_string ( <EOL> '<STR_LIT>' , email_context ) <EOL> email_content_plaintext = render_to_string ( <EOL> '<STR_LIT>' , email_context ) <EOL> format_dict = Format . FORMAT_DICT [ Format . CSV ] <EOL> excel_attachment = { <EOL> '<STR_LIT:title>' : '<STR_LIT>' % { <EOL> '<STR_LIT>' : first_of_month . strftime ( '<STR_LIT>' ) , <EOL> '<STR_LIT>' : format_dict [ '<STR_LIT>' ] , <EOL> } , <EOL> '<STR_LIT>' : format_dict [ '<STR_LIT>' ] , <EOL> '<STR_LIT>' : invoice . excel_response , <EOL> } <EOL> emails = emails or settings . BOOKKEEPER_CONTACT_EMAILS <EOL> for email in emails : <EOL> send_HTML_email ( <EOL> "<STR_LIT>" % datetime . date ( year , month , <NUM_LIT:1> ) . strftime ( USER_MONTH_FORMAT ) , <EOL> email , <EOL> email_content , <EOL> email_from = settings . DEFAULT_FROM_EMAIL , <EOL> text_content = email_content_plaintext , <EOL> file_attachments = [ excel_attachment ] , <EOL> ) <EOL> log_accounting_info ( <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" % { <EOL> '<STR_LIT>' : first_of_month . strftime ( USER_MONTH_FORMAT ) , <EOL> '<STR_LIT>' : "<STR_LIT:U+002CU+0020>" . join ( emails ) <EOL> } ) <EOL> @ periodic_task ( run_every = crontab ( minute = <NUM_LIT:0> , hour = <NUM_LIT:0> ) ) <EOL> def remind_subscription_ending ( ) : <EOL> """<STR_LIT>""" <EOL> send_subscription_reminder_emails ( <NUM_LIT:30> , exclude_trials = True ) <EOL> send_subscription_reminder_emails ( <NUM_LIT:10> , exclude_trials = True ) <EOL> send_subscription_reminder_emails ( <NUM_LIT:1> , exclude_trials = True ) <EOL> @ periodic_task ( run_every = crontab ( minute = <NUM_LIT:0> , hour = <NUM_LIT:0> ) ) <EOL> def remind_dimagi_contact_subscription_ending_40_days ( ) : <EOL> """<STR_LIT>""" <EOL> send_subscription_reminder_emails_dimagi_contact ( <NUM_LIT> ) <EOL> def send_subscription_reminder_emails ( num_days , exclude_trials = True ) : <EOL> today = datetime . date . today ( ) <EOL> date_in_n_days = today + datetime . timedelta ( days = num_days ) <EOL> ending_subscriptions = Subscription . objects . filter ( date_end = date_in_n_days , do_not_email = False ) <EOL> if exclude_trials : <EOL> ending_subscriptions = ending_subscriptions . filter ( is_trial = False ) <EOL> for subscription in ending_subscriptions : <EOL> try : <EOL> if not subscription . is_renewed : <EOL> subscription . send_ending_reminder_email ( ) <EOL> except Exception as e : <EOL> log_accounting_error ( e . message ) <EOL> def send_subscription_reminder_emails_dimagi_contact ( num_days ) : <EOL> today = datetime . date . today ( ) <EOL> date_in_n_days = today + datetime . timedelta ( days = num_days ) <EOL> ending_subscriptions = ( Subscription . objects <EOL> . filter ( is_active = True ) <EOL> . filter ( date_end = date_in_n_days ) <EOL> . filter ( do_not_email = False ) <EOL> . filter ( account__dimagi_contact__isnull = False ) ) <EOL> for subscription in ending_subscriptions : <EOL> if not subscription . is_renewed : <EOL> subscription . send_dimagi_ending_reminder_email ( ) <EOL> @ task ( ignore_result = True ) <EOL> def create_wire_credits_invoice ( domain_name , <EOL> account_created_by , <EOL> account_entry_point , <EOL> amount , <EOL> invoice_items , <EOL> contact_emails ) : <EOL> account = BillingAccount . get_or_create_account_by_domain ( <EOL> domain_name , <EOL> created_by = account_created_by , <EOL> entry_point = account_entry_point <EOL> ) [ <NUM_LIT:0> ] <EOL> wire_invoice = WirePrepaymentInvoice . objects . create ( <EOL> domain = domain_name , <EOL> date_start = datetime . datetime . utcnow ( ) , <EOL> date_end = datetime . datetime . utcnow ( ) , <EOL> date_due = None , <EOL> balance = amount , <EOL> account = account , <EOL> ) <EOL> wire_invoice . items = invoice_items <EOL> record = WirePrepaymentBillingRecord . generate_record ( wire_invoice ) <EOL> if record . should_send_email : <EOL> try : <EOL> record . send_email ( contact_emails = contact_emails ) <EOL> except Exception as e : <EOL> log_accounting_error ( e . message ) <EOL> else : <EOL> record . skipped_email = True <EOL> record . save ( ) <EOL> @ task ( ignore_result = True ) <EOL> def send_purchase_receipt ( payment_record , core_product , domain , <EOL> template_html , template_plaintext , <EOL> additional_context ) : <EOL> username = payment_record . payment_method . web_user <EOL> try : <EOL> web_user = WebUser . get_by_username ( username ) <EOL> email = web_user . get_email ( ) <EOL> name = web_user . first_name <EOL> except ResourceNotFound : <EOL> log_accounting_error ( <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" % username <EOL> ) <EOL> name = email = username <EOL> context = { <EOL> '<STR_LIT:name>' : name , <EOL> '<STR_LIT>' : fmt_dollar_amount ( payment_record . amount ) , <EOL> '<STR_LIT>' : domain , <EOL> '<STR_LIT>' : payment_record . date_created . strftime ( USER_DATE_FORMAT ) , <EOL> '<STR_LIT>' : core_product , <EOL> '<STR_LIT>' : payment_record . public_transaction_id , <EOL> } <EOL> context . update ( additional_context ) <EOL> email_html = render_to_string ( template_html , context ) <EOL> email_plaintext = render_to_string ( template_plaintext , context ) <EOL> send_HTML_email ( <EOL> ugettext ( "<STR_LIT>" ) , email , email_html , <EOL> text_content = email_plaintext , <EOL> email_from = get_dimagi_from_email_by_product ( core_product ) , <EOL> ) <EOL> @ task ( queue = '<STR_LIT>' , ignore_result = True ) <EOL> def send_autopay_failed ( invoice , payment_method ) : <EOL> subscription = invoice . subscription <EOL> auto_payer = subscription . account . auto_pay_user <EOL> payment_method = StripePaymentMethod . objects . get ( web_user = auto_payer ) <EOL> autopay_card = payment_method . get_autopay_card ( subscription . account ) <EOL> try : <EOL> recipient = WebUser . get_by_username ( auto_payer ) . get_email ( ) <EOL> except ResourceNotFound : <EOL> recipient = auto_payer <EOL> domain = invoice . get_domain ( ) <EOL> context = { <EOL> '<STR_LIT>' : domain , <EOL> '<STR_LIT>' : subscription . plan_version . plan . name , <EOL> '<STR_LIT>' : datetime . date . today ( ) , <EOL> '<STR_LIT>' : invoice . invoice_number , <EOL> '<STR_LIT>' : autopay_card , <EOL> '<STR_LIT>' : absolute_reverse ( '<STR_LIT>' , args = [ domain ] ) , <EOL> '<STR_LIT>' : absolute_reverse ( '<STR_LIT>' , args = [ domain ] ) , <EOL> '<STR_LIT>' : settings . INVOICING_CONTACT_EMAIL , <EOL> } <EOL> template_html = '<STR_LIT>' <EOL> template_plaintext = '<STR_LIT>' <EOL> send_HTML_email ( <EOL> subject = "<STR_LIT>" % invoice . invoice_number , <EOL> recipient = recipient , <EOL> html_content = render_to_string ( template_html , context ) , <EOL> text_content = render_to_string ( template_plaintext , context ) , <EOL> email_from = get_dimagi_from_email_by_product ( subscription . plan_version . product_rate . product . product_type ) , <EOL> ) <EOL> @ periodic_task ( run_every = crontab ( minute = <NUM_LIT:0> , hour = <NUM_LIT:0> , day_of_week = <NUM_LIT:1> ) ) <EOL> def weekly_digest ( ) : <EOL> today = datetime . date . today ( ) <EOL> in_forty_days = today + datetime . timedelta ( days = <NUM_LIT> ) <EOL> ending_in_forty_days = filter ( <EOL> lambda sub : not sub . is_renewed , <EOL> Subscription . objects . filter ( <EOL> date_end__lte = in_forty_days , <EOL> date_end__gte = today , <EOL> is_active = True , <EOL> is_trial = False , <EOL> account__dimagi_contact__isnull = True , <EOL> ) ) <EOL> if not ending_in_forty_days : <EOL> log_accounting_info ( <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> ) <EOL> return <EOL> table = [ [ <EOL> "<STR_LIT>" , "<STR_LIT>" , "<STR_LIT>" , "<STR_LIT>" , <EOL> "<STR_LIT>" , "<STR_LIT>" , "<STR_LIT>" , "<STR_LIT>" , <EOL> "<STR_LIT>" , <EOL> ] ] <EOL> def _fmt_row ( sub ) : <EOL> try : <EOL> created_by_adj = SubscriptionAdjustment . objects . filter ( <EOL> subscription = sub , <EOL> reason = SubscriptionAdjustmentReason . CREATE <EOL> ) . order_by ( '<STR_LIT>' ) [ <NUM_LIT:0> ] <EOL> created_by = dict ( SubscriptionAdjustmentMethod . CHOICES ) . get ( <EOL> created_by_adj . method , "<STR_LIT>" ) <EOL> except ( IndexError , SubscriptionAdjustment . DoesNotExist ) : <EOL> created_by = "<STR_LIT>" <EOL> return [ <EOL> sub . subscriber . domain , <EOL> "<STR_LIT>" % ( sub . account . name , sub . account . id ) , <EOL> sub . plan_version . plan . name , <EOL> sub . salesforce_contract_id , <EOL> sub . account . dimagi_contact , <EOL> sub . date_start , <EOL> sub . date_end , <EOL> "<STR_LIT>" if sub . do_not_invoice else "<STR_LIT>" , <EOL> created_by , <EOL> ] <EOL> table . extend ( [ _fmt_row ( sub ) for sub in ending_in_forty_days ] ) <EOL> file_to_attach = StringIO ( ) <EOL> export_from_tables ( <EOL> [ [ '<STR_LIT>' , table ] ] , <EOL> file_to_attach , <EOL> Format . XLS_2007 <EOL> ) <EOL> email_context = { <EOL> '<STR_LIT>' : today . isoformat ( ) , <EOL> '<STR_LIT>' : in_forty_days . isoformat ( ) , <EOL> } <EOL> email_content = render_to_string ( <EOL> '<STR_LIT>' , email_context ) <EOL> email_content_plaintext = render_to_string ( <EOL> '<STR_LIT>' , email_context ) <EOL> format_dict = Format . FORMAT_DICT [ Format . XLS_2007 ] <EOL> file_attachment = { <EOL> '<STR_LIT:title>' : '<STR_LIT>' % { <EOL> '<STR_LIT:start>' : today . isoformat ( ) , <EOL> '<STR_LIT:end>' : in_forty_days . isoformat ( ) , <EOL> } , <EOL> '<STR_LIT>' : format_dict [ '<STR_LIT>' ] , <EOL> '<STR_LIT>' : file_to_attach , <EOL> } <EOL> from_email = "<STR_LIT>" % settings . DEFAULT_FROM_EMAIL <EOL> env = ( "<STR_LIT>" . format ( settings . SERVER_ENVIRONMENT . upper ( ) ) <EOL> if settings . SERVER_ENVIRONMENT != "<STR_LIT>" else "<STR_LIT>" ) <EOL> email_subject = "<STR_LIT>" . format ( env , today . isoformat ( ) ) <EOL> send_HTML_email ( <EOL> email_subject , <EOL> settings . ACCOUNTS_EMAIL , <EOL> email_content , <EOL> email_from = from_email , <EOL> text_content = email_content_plaintext , <EOL> file_attachments = [ file_attachment ] , <EOL> ) <EOL> log_accounting_info ( <EOL> "<STR_LIT>" % { <EOL> '<STR_LIT>' : today . isoformat ( ) , <EOL> } ) <EOL> @ periodic_task ( run_every = crontab ( hour = <NUM_LIT:0> <NUM_LIT:1> , minute = <NUM_LIT:0> , ) ) <EOL> def pay_autopay_invoices ( ) : <EOL> """<STR_LIT>""" <EOL> AutoPayInvoicePaymentHandler ( ) . pay_autopayable_invoices ( datetime . datetime . today ( ) ) <EOL> @ periodic_task ( run_every = crontab ( minute = <NUM_LIT:0> , hour = <NUM_LIT:0> ) , queue = '<STR_LIT>' ) <EOL> def update_exchange_rates ( app_id = settings . OPEN_EXCHANGE_RATES_API_ID ) : <EOL> try : <EOL> log_accounting_info ( "<STR_LIT>" ) <EOL> rates = json . load ( urllib2 . urlopen ( <EOL> '<STR_LIT>' % app_id ) ) [ '<STR_LIT>' ] <EOL> default_rate = float ( rates [ Currency . get_default ( ) . code ] ) <EOL> for code , rate in rates . items ( ) : <EOL> currency , _ = Currency . objects . get_or_create ( code = code ) <EOL> currency . rate_to_default = float ( rate ) / default_rate <EOL> currency . save ( ) <EOL> log_accounting_info ( "<STR_LIT>" % { <EOL> '<STR_LIT:code>' : currency . code , <EOL> '<STR_LIT>' : currency . rate_to_default , <EOL> } ) <EOL> except Exception as e : <EOL> log_accounting_error ( "<STR_LIT>" % e . message ) </s>
