def book_classes(self, csID, date_from, date_until=None): """ :param csID: db.customers_subscriptions.id :param date_from: datetime.date :param date_until: datetime.date :return: Integer - number of classes booked """ from os_attendance_helper import AttendanceHelper from os_customer import Customer from os_customer_subscription import CustomerSubscription # Check subscription credits, if none, don't do anything credits = 0 customer = Customer(self.row.auth_customer_id) cs = CustomerSubscription(csID) credits += cs.get_credits_balance() # Get list of classes for customer in a given month, based on reservations upcoming_classes = self.get_classes(date_from, date_until) ah = AttendanceHelper() classes_booked = 0 while credits > 0 and len(upcoming_classes): # Sign in to a class ## # remove this reservation from the list, as we have just booked it, so it won't be booked again using # another subscription ## cls = upcoming_classes.pop( 0 ) # always get the first in the list, we pop all classes already booked ah.attendance_sign_in_subscription(self.row.auth_customer_id, cls['clsID'], cs.cs.id, cls['date']) classes_booked += 1 # Subtract one credit from current balance in this object (self.add_credists_balance) credits -= 1 return classes_booked
def customers_subscriptions_create_invoices_for_month( self, year, month, description, invoice_date='today'): """ Actually create invoices for subscriptions for a given month """ from os_customer_subscription import CustomerSubscription from general_helpers import get_last_day_month from os_invoice import Invoice T = current.T db = current.db DATE_FORMAT = current.DATE_FORMAT year = int(year) month = int(month) firstdaythismonth = datetime.date(year, month, 1) lastdaythismonth = get_last_day_month(firstdaythismonth) invoices_count = 0 # get all active subscriptions in month query = (db.customers_subscriptions.Startdate <= lastdaythismonth) & \ ((db.customers_subscriptions.Enddate >= firstdaythismonth) | (db.customers_subscriptions.Enddate == None)) rows = db(query).select(db.customers_subscriptions.ALL) for row in rows: cs = CustomerSubscription(row.id) cs.create_invoice_for_month(year, month, description, invoice_date) invoices_count += 1 ## # For scheduled tasks db connection has to be committed manually ## db.commit() return T("Invoices in month") + ': ' + unicode(invoices_count)
def _add_get_form_enable_subscription_fields(self, csID): """ Enable fields required for subscriptions """ from os_customer_subscription import CustomerSubscription db = current.db cs = CustomerSubscription(csID) db.invoices.payment_methods_id.default = cs.payment_methods_id db.invoices.SubscriptionYear.readable = True db.invoices.SubscriptionYear.writable = True db.invoices.SubscriptionMonth.readable = True db.invoices.SubscriptionMonth.writable = True
def add_subscription_credits_month(self, csID, cuID, year, month, p_start, p_end, classes, subscription_unit, batch_add=True, book_classes=True): """ Insert subscription credits and clear cache for customer subscription :param csID: db.customers_subscriptions.id :param cuID: db.auth_user.id :param year: int :param month: int :param p_start: datetime.date (Period start) :param p_end: datetime.date (Period end) :param classes: int :param subscription_unit: string either 'week' or 'month' :return: None """ from os_attendance_helper import AttendanceHelper from os_customer_subscription import CustomerSubscription T = current.T db = current.db now = current.NOW_LOCAL cache_clear_customers_subscriptions = current.globalenv[ 'cache_clear_customers_subscriptions'] TODAY_LOCAL = current.TODAY_LOCAL first_day = datetime.date(year, month, 1) last_day = get_last_day_month(first_day) t_days = (last_day - first_day) + datetime.timedelta( days=1) # Total days (Add 1, when subsctraced it's one day less) p_days = (p_end - p_start) + datetime.timedelta(days=1) # Period days percent = float(p_days.days) / float(t_days.days) if subscription_unit == 'month': credits = round(classes * percent, 1) else: weeks_in_month = round(t_days.days / float(7), 1) credits = round((weeks_in_month * (classes or 0)) * percent, 1) db.customers_subscriptions_credits.insert( customers_subscriptions_id=csID, MutationDateTime=now, MutationType='add', MutationAmount=credits, Description=T('Credits') + ' ' + first_day.strftime('%B %Y'), SubscriptionYear=year, SubscriptionMonth=month) if batch_add: try: self.add_credits_balance[cuID] += credits except KeyError: self.add_credits_balance[cuID] = credits else: cs = CustomerSubscription(csID) self.add_credits_balance[cuID] = cs.get_credits_balance() if book_classes: # Get list of classes for customer in a given month, based on reservations classes_this_month = self.add_credits_reservations.get(cuID, False) ah = AttendanceHelper() if classes_this_month: # Book classess while len(self.add_credits_reservations.get( cuID)) > 0 and self.add_credits_balance[cuID] > 0: # Sign in to a class ## # remove this reservation from the list, as we have just booked it, so it won't be booked again using # another subscription ## reservation = self.add_credits_reservations[cuID].pop( 0 ) # always get the first in the list, we pop all classes already booked ah.attendance_sign_in_subscription(cuID, reservation['clsID'], csID, reservation['date']) # Subtract one credit from current balance in this object (self.add_credits_balance) self.add_credits_balance[cuID] -= 1 # Clear cache cache_clear_customers_subscriptions(cuID)
def item_add_subscription(self, SubscriptionYear, SubscriptionMonth, description=''): """ :param SubscriptionYear: Year of subscription :param SubscriptionMonth: Month of subscription :return: db.invoices_items.id """ from general_helpers import get_last_day_month from os_customer_subscription import CustomerSubscription from os_school_subscription import SchoolSubscription db = current.db DATE_FORMAT = current.DATE_FORMAT next_sort_nr = self.get_item_next_sort_nr() date = datetime.date(int(SubscriptionYear), int(SubscriptionMonth), 1) ics = db.invoices_customers_subscriptions(invoices_id=self.invoices_id) csID = ics.customers_subscriptions_id cs = CustomerSubscription(csID) ssuID = cs.ssuID ssu = SchoolSubscription(ssuID) row = ssu.get_tax_rates_on_date(date) if row: tax_rates_id = row.school_subscriptions_price.tax_rates_id else: tax_rates_id = None period_start = date period_end = get_last_day_month(date) glaccount = ssu.get_glaccount_on_date(date) costcenter = ssu.get_costcenter_on_date(date) price = 0 # check for alt price csap = db.customers_subscriptions_alt_prices query = (csap.customers_subscriptions_id == csID) & \ (csap.SubscriptionYear == SubscriptionYear) & \ (csap.SubscriptionMonth == SubscriptionMonth) csap_rows = db(query).select(csap.ALL) if csap_rows: csap_row = csap_rows.first() price = csap_row.Amount description = csap_row.Description else: price = ssu.get_price_on_date(date, False) broken_period = False if cs.startdate > date and cs.startdate <= period_end: # Start later in month broken_period = True period_start = cs.startdate delta = period_end - cs.startdate cs_days = delta.days + 1 total_days = period_end.day if cs.enddate: if cs.enddate >= date and cs.enddate < period_end: # End somewhere in month broken_period = True delta = cs.enddate - date cs_days = delta.days + 1 total_days = period_end.day period_end = cs.enddate if broken_period: price = round( float(cs_days) / float(total_days) * float(price), 2) if not description: description = cs.name.decode( 'utf-8') + u' ' + period_start.strftime( DATE_FORMAT) + u' - ' + period_end.strftime( DATE_FORMAT) iiID = db.invoices_items.insert(invoices_id=self.invoices_id, ProductName=current.T("Subscription") + ' ' + unicode(csID), Description=description, Quantity=1, Price=price, Sorting=next_sort_nr, tax_rates_id=tax_rates_id, accounting_glaccounts_id=glaccount, accounting_costcenters_id=costcenter) ## # Check if a registration fee should be added ## query = ( ((db.customers_subscriptions.auth_customer_id == cs.auth_customer_id) & (db.customers_subscriptions.id != cs.csID) & (db.customers_subscriptions.school_subscriptions_id == cs.ssuID)) | (db.customers_subscriptions.RegistrationFeePaid == True)) fee_paid_in_past = db(query).count() ssu = db.school_subscriptions(ssuID) if not fee_paid_in_past and ssu.RegistrationFee: # Registration fee not already paid and RegistrationFee defined? regfee_to_be_paid = ssu.RegistrationFee or 0 if regfee_to_be_paid: db.invoices_items.insert( invoices_id=self.invoices_id, ProductName=current.T("Registration fee"), Description=current.T('One time registration fee'), Quantity=1, Price=regfee_to_be_paid, Sorting=next_sort_nr, tax_rates_id=tax_rates_id, ) # Mark registration fee as paid for subscription db.customers_subscriptions[cs.csID] = dict( RegistrationFeePaid=True) ## # Always call these ## # This calls self.on_update() self.set_amounts() return iiID
def deliver(self, class_online_booking=True, class_booking_status='booked'): """ Create invoice for order and deliver goods """ from os_attendance_helper import AttendanceHelper from os_cache_manager import OsCacheManager from os_invoice import Invoice from os_school_classcard import SchoolClasscard from os_school_subscription import SchoolSubscription from os_customer_subscription import CustomerSubscription from os_school_membership import SchoolMembership from os_customer_membership import CustomerMembership from os_workshop import Workshop from os_workshop_product import WorkshopProduct cache_clear_classschedule_api = current.globalenv[ 'cache_clear_classschedule_api'] get_sys_property = current.globalenv['get_sys_property'] TODAY_LOCAL = current.TODAY_LOCAL ocm = OsCacheManager() db = current.db T = current.T if self.order.Status == 'delivered': return create_invoice = False iID = None invoice = None # Only create an invoice if order amount > 0 amounts = self.get_amounts() sys_property_create_invoice = 'shop_donations_create_invoice' create_invoice_for_donations = get_sys_property( sys_property_create_invoice) if create_invoice_for_donations == 'on': create_invoice_for_donations = True else: create_invoice_for_donations = False if amounts: if amounts.TotalPriceVAT > 0: if not self.order.Donation or (self.order.Donation and create_invoice_for_donations): create_invoice = True # Create blank invoice igpt = db.invoices_groups_product_types(ProductType='shop') iID = db.invoices.insert( invoices_groups_id=igpt.invoices_groups_id, Description=T('Order #') + unicode(self.coID), Status='sent') # Link invoice to order db.invoices_customers_orders.insert( customers_orders_id=self.coID, invoices_id=iID) # Call init function for invoices to set Invoice # , etc. invoice = Invoice(iID) invoice.link_to_customer(self.order.auth_customer_id) # Add items to the invoice rows = self.get_order_items_rows() for row in rows: ## # Only rows where school_classcards_id, workshops_products_id , classes_id or Donation are set # are put on the invoice ## # Check for product: if row.ProductVariant: if create_invoice: invoice.item_add_product_variant( product_name=row.ProductName, description=row.Description, quantity=row.Quantity, price=row.Price, tax_rates_id=row.tax_rates_id, accounting_glaccounts_id=row.accounting_glaccounts_id, accounting_costcenters_id=row.accounting_costcenters_id ) # Check for classcard if row.school_classcards_id: # Deliver card card_start = TODAY_LOCAL scd = SchoolClasscard(row.school_classcards_id) ccdID = scd.sell_to_customer(self.order.auth_customer_id, card_start, invoice=False) # clear cache ocm.clear_customers_classcards(self.order.auth_customer_id) # Add card to invoice if create_invoice: invoice.item_add_classcard(ccdID) # Check for subscription if row.school_subscriptions_id: ## Deliver subscription # Determine payment method cs_payment_method = get_sys_property( 'shop_subscriptions_payment_method') if cs_payment_method == 'mollie': payment_method_id = 100 else: payment_method_id = 3 subscription_start = TODAY_LOCAL ssu = SchoolSubscription(row.school_subscriptions_id) csID = ssu.sell_to_customer( self.order.auth_customer_id, subscription_start, payment_methods_id=payment_method_id) # Add credits for the first month cs = CustomerSubscription(csID) cs.add_credits_month(subscription_start.year, subscription_start.month) # clear cache ocm.clear_customers_subscriptions(self.order.auth_customer_id) if create_invoice: # This will also add the registration fee if required. iiID = invoice.item_add_subscription( csID, TODAY_LOCAL.year, TODAY_LOCAL.month) # Check for membership if row.school_memberships_id: # Deliver membership membership_start = TODAY_LOCAL sme = SchoolMembership(row.school_memberships_id) cmID = sme.sell_to_customer( self.order.auth_customer_id, membership_start, invoice=False, # Don't create a separate invoice ) # clear cache ocm.clear_customers_memberships(self.order.auth_customer_id) if create_invoice: cm = CustomerMembership(cmID) # Check if price exists and > 0: if sme.row.Price: iiID = invoice.item_add_membership(cmID) # Check for workshop if row.workshops_products_id: # Deliver workshop product wsp = WorkshopProduct(row.workshops_products_id) wspcID = wsp.sell_to_customer(self.order.auth_customer_id, invoice=False) # Add workshop product to invoice if create_invoice: invoice.item_add_workshop_product(wspcID) # Check if sold out if wsp.is_sold_out(): # Cancel all unpaid orders with a sold out product for this workshop ws = Workshop(wsp.wsID) ws.cancel_orders_with_sold_out_products() # Check for classes if row.classes_id: # Deliver class ah = AttendanceHelper() if row.AttendanceType == 1: result = ah.attendance_sign_in_trialclass( self.order.auth_customer_id, row.classes_id, row.ClassDate, online_booking=class_online_booking, invoice=False, booking_status=class_booking_status) elif row.AttendanceType == 2: result = ah.attendance_sign_in_dropin( self.order.auth_customer_id, row.classes_id, row.ClassDate, online_booking=class_online_booking, invoice=False, booking_status=class_booking_status, ) if create_invoice: invoice.item_add_class_from_order(row, result['caID']) # Clear api cache to update available spaces cache_clear_classschedule_api() # Check for donation if row.Donation: # Add donation line to invoice if create_invoice and create_invoice_for_donations: invoice.item_add_donation(row.TotalPriceVAT, row.Description) # Check for custom item if row.Custom: # Add custom line to invoice if create_invoice: invoice.item_add_custom_from_order(row) # Notify customer of new invoice #if create_invoice: #invoice.mail_customer_invoice_created() # Update status self.set_status_delivered() # Notify customer of order delivery self._deliver_mail_customer() return dict(iID=iID, invoice=invoice)
def item_add_subscription(self, csID, SubscriptionYear, SubscriptionMonth, description=''): """ :param SubscriptionYear: Year of subscription :param SubscriptionMonth: Month of subscription :return: db.invoices_items.id """ T = current.T from general_helpers import get_last_day_month from os_customer_subscription import CustomerSubscription from os_school_subscription import SchoolSubscription db = current.db DATE_FORMAT = current.DATE_FORMAT next_sort_nr = self.get_item_next_sort_nr() date = datetime.date(int(SubscriptionYear), int(SubscriptionMonth), 1) cs = CustomerSubscription(csID) ssuID = cs.ssuID ssu = SchoolSubscription(ssuID) row = ssu.get_tax_rates_on_date(date) if row: tax_rates_id = row.school_subscriptions_price.tax_rates_id else: tax_rates_id = None period_start = date first_day_month = date last_day_month = get_last_day_month(date) period_end = last_day_month glaccount = ssu.get_glaccount_on_date(date) costcenter = ssu.get_costcenter_on_date(date) price = 0 # check for alt price csap = db.customers_subscriptions_alt_prices query = (csap.customers_subscriptions_id == csID) & \ (csap.SubscriptionYear == SubscriptionYear) & \ (csap.SubscriptionMonth == SubscriptionMonth) csap_rows = db(query).select(csap.ALL) if csap_rows: # alt. price overrides broken period csap_row = csap_rows.first() price = csap_row.Amount description = csap_row.Description else: price = ssu.get_price_on_date(date, False) broken_period = False pause = False # Check pause query = (db.customers_subscriptions_paused.customers_subscriptions_id == csID) & \ (db.customers_subscriptions_paused.Startdate <= last_day_month) & \ ((db.customers_subscriptions_paused.Enddate >= first_day_month) | (db.customers_subscriptions_paused.Enddate == None)) rows = db(query).select(db.customers_subscriptions_paused.ALL) if rows: pause = rows.first() # Calculate days to be paid if cs.startdate > first_day_month and cs.startdate <= last_day_month: # Start later in month broken_period = True period_start = cs.startdate if cs.enddate: if cs.enddate >= first_day_month and cs.enddate < last_day_month: # End somewhere in month broken_period = True period_end = cs.enddate Range = namedtuple('Range', ['start', 'end']) period_range = Range(start=period_start, end=period_end) period_days = (period_range.end - period_range.start).days + 1 if pause: pause_range = Range(start=pause.Startdate, end=pause.Enddate) latest_start = max(period_range.start, pause_range.start) earliest_end = min(pause_range.end, pause_range.end) delta = (earliest_end - latest_start).days + 1 overlap = max(0, delta) # Subtract pause overlap from period to be paid period_days = period_days - overlap month_days = (last_day_month - first_day_month).days + 1 price = round(((float(period_days) / float(month_days)) * float(price)), 2) if not description: description = cs.name.decode('utf-8') + u' ' + period_start.strftime(DATE_FORMAT) + u' - ' + period_end.strftime(DATE_FORMAT) if pause: description += u'\n' description += u"(" + T("Pause") + u": " description += pause.Startdate.strftime(DATE_FORMAT) + u" - " description += pause.Enddate.strftime(DATE_FORMAT) + u" | " description += T("Days paid this period: ") description += unicode(period_days) description += u")" iiID = db.invoices_items.insert( invoices_id = self.invoices_id, ProductName = current.T("Subscription") + ' ' + unicode(csID), Description = description, Quantity = 1, Price = price, Sorting = next_sort_nr, tax_rates_id = tax_rates_id, accounting_glaccounts_id = glaccount, accounting_costcenters_id = costcenter ) ## # Check if a registration fee should be added ## query = (((db.customers_subscriptions.auth_customer_id == cs.auth_customer_id) & (db.customers_subscriptions.id != cs.csID) & (db.customers_subscriptions.school_subscriptions_id == cs.ssuID)) | (db.customers_subscriptions.RegistrationFeePaid == True)) fee_paid_in_past = db(query).count() ssu = db.school_subscriptions(ssuID) if not fee_paid_in_past and ssu.RegistrationFee: # Registration fee not already paid and RegistrationFee defined? regfee_to_be_paid = ssu.RegistrationFee or 0 if regfee_to_be_paid: db.invoices_items.insert( invoices_id = self.invoices_id, ProductName = current.T("Registration fee"), Description = current.T('One time registration fee'), Quantity = 1, Price = regfee_to_be_paid, Sorting = next_sort_nr, tax_rates_id = tax_rates_id, ) # Mark registration fee as paid for subscription db.customers_subscriptions[cs.csID] = dict(RegistrationFeePaid=True) ## # Always call these ## # Link invoice item to subscription self.link_item_to_customer_subscription(csID, iiID) # This calls self.on_update() self.set_amounts() return iiID