示例#1
0
    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)
示例#3
0
    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)
示例#5
0
    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
示例#6
0
    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)
示例#7
0
    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