def process_downgrade(plan: CustomerPlan) -> None: from zerver.lib.actions import do_change_realm_plan_type assert plan.customer.realm is not None do_change_realm_plan_type(plan.customer.realm, Realm.PLAN_TYPE_LIMITED, acting_user=None) plan.status = CustomerPlan.ENDED plan.save(update_fields=["status"])
def do_change_plan_status(plan: CustomerPlan, status: int) -> None: plan.status = status plan.save(update_fields=['status']) billing_logger.info( 'Change plan status: Customer.id: %s, CustomerPlan.id: %s, status: %s', plan.customer.id, plan.id, status, )
def make_end_of_cycle_updates_if_needed(plan: CustomerPlan, event_time: datetime) -> Optional[LicenseLedger]: last_ledger_entry = LicenseLedger.objects.filter(plan=plan).order_by('-id').first() last_renewal = LicenseLedger.objects.filter(plan=plan, is_renewal=True) \ .order_by('-id').first().event_time next_billing_cycle = start_of_next_billing_cycle(plan, last_renewal) if next_billing_cycle <= event_time: if plan.status == CustomerPlan.ACTIVE: return LicenseLedger.objects.create( plan=plan, is_renewal=True, event_time=next_billing_cycle, licenses=last_ledger_entry.licenses_at_next_renewal, licenses_at_next_renewal=last_ledger_entry.licenses_at_next_renewal) if plan.status == CustomerPlan.FREE_TRIAL: plan.invoiced_through = last_ledger_entry assert(plan.next_invoice_date is not None) plan.billing_cycle_anchor = plan.next_invoice_date.replace(microsecond=0) plan.status = CustomerPlan.ACTIVE plan.save(update_fields=["invoiced_through", "billing_cycle_anchor", "status"]) return LicenseLedger.objects.create( plan=plan, is_renewal=True, event_time=next_billing_cycle, licenses=last_ledger_entry.licenses_at_next_renewal, licenses_at_next_renewal=last_ledger_entry.licenses_at_next_renewal) if plan.status == CustomerPlan.DOWNGRADE_AT_END_OF_CYCLE: process_downgrade(plan) return None return last_ledger_entry
def process_downgrade(plan: CustomerPlan) -> None: from zerver.lib.actions import do_change_plan_type do_change_plan_type(plan.customer.realm, Realm.LIMITED) plan.status = CustomerPlan.ENDED plan.save(update_fields=['status'])
def make_end_of_cycle_updates_if_needed( plan: CustomerPlan, event_time: datetime ) -> Tuple[Optional[CustomerPlan], Optional[LicenseLedger]]: last_ledger_entry = LicenseLedger.objects.filter( plan=plan).order_by('-id').first() last_renewal = LicenseLedger.objects.filter(plan=plan, is_renewal=True) \ .order_by('-id').first().event_time next_billing_cycle = start_of_next_billing_cycle(plan, last_renewal) if next_billing_cycle <= event_time: if plan.status == CustomerPlan.ACTIVE: return None, LicenseLedger.objects.create( plan=plan, is_renewal=True, event_time=next_billing_cycle, licenses=last_ledger_entry.licenses_at_next_renewal, licenses_at_next_renewal=last_ledger_entry. licenses_at_next_renewal) if plan.status == CustomerPlan.FREE_TRIAL: plan.invoiced_through = last_ledger_entry assert (plan.next_invoice_date is not None) plan.billing_cycle_anchor = plan.next_invoice_date.replace( microsecond=0) plan.status = CustomerPlan.ACTIVE plan.save(update_fields=[ "invoiced_through", "billing_cycle_anchor", "status" ]) return None, LicenseLedger.objects.create( plan=plan, is_renewal=True, event_time=next_billing_cycle, licenses=last_ledger_entry.licenses_at_next_renewal, licenses_at_next_renewal=last_ledger_entry. licenses_at_next_renewal) if plan.status == CustomerPlan.SWITCH_TO_ANNUAL_AT_END_OF_CYCLE: if plan.fixed_price is not None: # nocoverage raise NotImplementedError( "Can't switch fixed priced monthly plan to annual.") plan.status = CustomerPlan.ENDED plan.save(update_fields=["status"]) discount = plan.customer.default_discount or plan.discount _, _, _, price_per_license = compute_plan_parameters( automanage_licenses=plan.automanage_licenses, billing_schedule=CustomerPlan.ANNUAL, discount=plan.discount) new_plan = CustomerPlan.objects.create( customer=plan.customer, billing_schedule=CustomerPlan.ANNUAL, automanage_licenses=plan.automanage_licenses, charge_automatically=plan.charge_automatically, price_per_license=price_per_license, discount=discount, billing_cycle_anchor=next_billing_cycle, tier=plan.tier, status=CustomerPlan.ACTIVE, next_invoice_date=next_billing_cycle, invoiced_through=None, invoicing_status=CustomerPlan.INITIAL_INVOICE_TO_BE_SENT, ) new_plan_ledger_entry = LicenseLedger.objects.create( plan=new_plan, is_renewal=True, event_time=next_billing_cycle, licenses=last_ledger_entry.licenses_at_next_renewal, licenses_at_next_renewal=last_ledger_entry. licenses_at_next_renewal) RealmAuditLog.objects.create( realm=new_plan.customer.realm, event_time=event_time, event_type=RealmAuditLog. CUSTOMER_SWITCHED_FROM_MONTHLY_TO_ANNUAL_PLAN, extra_data=orjson.dumps({ "monthly_plan_id": plan.id, "annual_plan_id": new_plan.id, }).decode()) return new_plan, new_plan_ledger_entry if plan.status == CustomerPlan.DOWNGRADE_AT_END_OF_CYCLE: process_downgrade(plan) return None, None return None, last_ledger_entry
def make_end_of_cycle_updates_if_needed( plan: CustomerPlan, event_time: datetime ) -> Tuple[Optional[CustomerPlan], Optional[LicenseLedger]]: last_ledger_entry = LicenseLedger.objects.filter(plan=plan).order_by("-id").first() last_ledger_renewal = ( LicenseLedger.objects.filter(plan=plan, is_renewal=True).order_by("-id").first() ) assert last_ledger_renewal is not None last_renewal = last_ledger_renewal.event_time if plan.is_free_trial() or plan.status == CustomerPlan.SWITCH_NOW_FROM_STANDARD_TO_PLUS: assert plan.next_invoice_date is not None next_billing_cycle = plan.next_invoice_date else: next_billing_cycle = start_of_next_billing_cycle(plan, last_renewal) if next_billing_cycle <= event_time and last_ledger_entry is not None: licenses_at_next_renewal = last_ledger_entry.licenses_at_next_renewal assert licenses_at_next_renewal is not None if plan.status == CustomerPlan.ACTIVE: return None, LicenseLedger.objects.create( plan=plan, is_renewal=True, event_time=next_billing_cycle, licenses=licenses_at_next_renewal, licenses_at_next_renewal=licenses_at_next_renewal, ) if plan.is_free_trial(): plan.invoiced_through = last_ledger_entry plan.billing_cycle_anchor = next_billing_cycle.replace(microsecond=0) plan.status = CustomerPlan.ACTIVE plan.save(update_fields=["invoiced_through", "billing_cycle_anchor", "status"]) return None, LicenseLedger.objects.create( plan=plan, is_renewal=True, event_time=next_billing_cycle, licenses=licenses_at_next_renewal, licenses_at_next_renewal=licenses_at_next_renewal, ) if plan.status == CustomerPlan.SWITCH_TO_ANNUAL_AT_END_OF_CYCLE: if plan.fixed_price is not None: # nocoverage raise NotImplementedError("Can't switch fixed priced monthly plan to annual.") plan.status = CustomerPlan.ENDED plan.save(update_fields=["status"]) discount = plan.customer.default_discount or plan.discount _, _, _, price_per_license = compute_plan_parameters( tier=plan.tier, automanage_licenses=plan.automanage_licenses, billing_schedule=CustomerPlan.ANNUAL, discount=plan.discount, ) new_plan = CustomerPlan.objects.create( customer=plan.customer, billing_schedule=CustomerPlan.ANNUAL, automanage_licenses=plan.automanage_licenses, charge_automatically=plan.charge_automatically, price_per_license=price_per_license, discount=discount, billing_cycle_anchor=next_billing_cycle, tier=plan.tier, status=CustomerPlan.ACTIVE, next_invoice_date=next_billing_cycle, invoiced_through=None, invoicing_status=CustomerPlan.INITIAL_INVOICE_TO_BE_SENT, ) new_plan_ledger_entry = LicenseLedger.objects.create( plan=new_plan, is_renewal=True, event_time=next_billing_cycle, licenses=licenses_at_next_renewal, licenses_at_next_renewal=licenses_at_next_renewal, ) realm = new_plan.customer.realm assert realm is not None RealmAuditLog.objects.create( realm=realm, event_time=event_time, event_type=RealmAuditLog.CUSTOMER_SWITCHED_FROM_MONTHLY_TO_ANNUAL_PLAN, extra_data=orjson.dumps( { "monthly_plan_id": plan.id, "annual_plan_id": new_plan.id, } ).decode(), ) return new_plan, new_plan_ledger_entry if plan.status == CustomerPlan.SWITCH_NOW_FROM_STANDARD_TO_PLUS: standard_plan = plan standard_plan.end_date = next_billing_cycle standard_plan.status = CustomerPlan.ENDED standard_plan.save(update_fields=["status", "end_date"]) (_, _, _, plus_plan_price_per_license) = compute_plan_parameters( CustomerPlan.PLUS, standard_plan.automanage_licenses, standard_plan.billing_schedule, standard_plan.customer.default_discount, ) plus_plan_billing_cycle_anchor = standard_plan.end_date.replace(microsecond=0) plus_plan = CustomerPlan.objects.create( customer=standard_plan.customer, status=CustomerPlan.ACTIVE, automanage_licenses=standard_plan.automanage_licenses, charge_automatically=standard_plan.charge_automatically, price_per_license=plus_plan_price_per_license, discount=standard_plan.customer.default_discount, billing_schedule=standard_plan.billing_schedule, tier=CustomerPlan.PLUS, billing_cycle_anchor=plus_plan_billing_cycle_anchor, invoicing_status=CustomerPlan.INITIAL_INVOICE_TO_BE_SENT, next_invoice_date=plus_plan_billing_cycle_anchor, ) standard_plan_last_ledger = ( LicenseLedger.objects.filter(plan=standard_plan).order_by("id").last() ) licenses_for_plus_plan = standard_plan_last_ledger.licenses_at_next_renewal plus_plan_ledger_entry = LicenseLedger.objects.create( plan=plus_plan, is_renewal=True, event_time=plus_plan_billing_cycle_anchor, licenses=licenses_for_plus_plan, licenses_at_next_renewal=licenses_for_plus_plan, ) return plus_plan, plus_plan_ledger_entry if plan.status == CustomerPlan.DOWNGRADE_AT_END_OF_CYCLE: process_downgrade(plan) return None, None return None, last_ledger_entry