def offers():
    """
    Page to accept and decline substitution requests
    :return:
    """
    from openstudio.os_class import Class
    from openstudio.os_classes_otc_sub_availables import ClassesOTCSubAvailables

    cotcID = request.vars['cotcID']
    cotc = db.classes_otc(cotcID)
    cls = Class(cotc.classes_id, cotc.ClassDate)

    response.title = T('Classes')
    response.subtitle = T('Sub teachers')
    response.view = 'general/tabs_menu.html'

    subs_avail = ClassesOTCSubAvailables()
    table = subs_avail.list_formatted(cotcID)

    content = DIV(
        H4(T('Sub offers')),
        H5(cls.get_name()),
        table
    )

    back = os_gui.get_button(
        'back',
        URL('index')
    )

    return dict(
        content=content,
        menu=get_menu('index'),
        back=back
    )
Exemple #2
0
def checkin_booking_options():
    """
        List booking options for a customer
    """
    cuID = request.vars['cuID']
    clsID = request.vars['clsID']
    date_formatted = request.vars['date']
    date = datestr_to_python(DATE_FORMAT, date_formatted)

    customer = Customer(cuID)
    cls = Class(clsID, date)
    starttime = cls.cls.Starttime.strftime(TIME_FORMAT)
    pretty_date = date.strftime('%B %d, %Y')
    classtype = db.school_classtypes(cls.cls.school_classtypes_id)
    location = db.school_locations(cls.cls.school_locations_id)

    response.title = T(classtype.Name)
    response.subtitle = SPAN(starttime, ' ', pretty_date)
    response.view = 'selfcheckin/checkin.html'

    return_url = URL('selfcheckin',
                     'checkin',
                     vars={
                         'clsID': clsID,
                         'date': date_formatted
                     })

    cls = Class(clsID, date)

    ah = AttendanceHelper()
    options = ah.get_customer_class_booking_options_formatted(
        clsID,
        date,
        customer,
        trial=True,
        list_type='selfcheckin',
        controller='classes')
    cancel = os_gui.get_button('noicon',
                               return_url,
                               title=T('Cancel'),
                               btn_size='')

    content = DIV(H3(T('Booking options for'),
                     ' ',
                     customer.row.display_name,
                     _class='center'),
                  BR(),
                  options,
                  DIV(BR(), cancel, _class="col-md-12 center"),
                  _class="row")

    back = os_gui.get_button('back', return_url)

    return dict(content=content, back=back)
    def email_reminders_teachers_sub_request_open(self):
        """
        Send teachers reminders when a sub for their class hasn't been found yet.
        :return:
        """
        from openstudio.os_class import Class
        from openstudio.os_mail import OsMail
        from openstudio.os_sys_email_reminders import SysEmailReminders

        T = current.T
        db = current.db
        TODAY_LOCAL = current.TODAY_LOCAL

        # Check if reminders configured
        sys_reminders = SysEmailReminders('teachers_sub_request_open')
        reminders = sys_reminders.list()

        mails_sent = 0
        for reminder in reminders:
            # Get list of open classes on reminder date
            reminder_date = TODAY_LOCAL + datetime.timedelta(reminder.Days)

            query = (db.classes_otc.Status == 'open') & \
                    (db.classes_otc.ClassDate == reminder_date)

            rows = db(query).select(db.classes_otc.ALL)
            for row in rows:
                clsID = row.classes_id
                cls = Class(clsID, row.ClassDate)
                regular_teachers = cls.get_regular_teacher_ids()

                if not regular_teachers['error']:
                    auth_teacher_id = regular_teachers['auth_teacher_id']
                    teacher = db.auth_user(auth_teacher_id)

                    os_mail = OsMail()
                    result = os_mail.render_email_template(
                        'teacher_sub_request_open_reminder',
                        classes_otc_id=row.id,
                        return_html=True)

                    send_result = False
                    if not result['error']:
                        send_result = os_mail.send(
                            message_html=result['html_message'],
                            message_subject=T("Reminder - open class"),
                            auth_user_id=auth_teacher_id)

                    if send_result:
                        mails_sent += 1

            # send reminder to teacher

        return "Sent mails: %s" % mails_sent
Exemple #4
0
    def _render_email_template_teacher_sub_request_open_reminder(
            self, template_content, cotcID):
        """
        Render mail template for teacher no sub found yet reminders
        :param cotcID: db.classes_otc.id
        :return: html - mail body for reminder
        """
        from openstudio.os_class import Class

        T = current.T
        db = current.db
        DATE_FORMAT = current.DATE_FORMAT
        TIME_FORMAT = current.TIME_FORMAT

        error = False
        error_msg = ''

        cotc = db.classes_otc(cotcID)

        cls = Class(cotc.classes_id, cotc.ClassDate)
        regular_teachers = cls.get_regular_teacher_ids()
        teacher_name = ''
        if not regular_teachers['error']:
            auth_teacher_id = regular_teachers['auth_teacher_id']
            teacher = db.auth_user(auth_teacher_id)
            teacher_name = teacher.first_name

        class_info = TABLE(TR(
            TH(T('Date'), _align="right"),
            TD(cotc.ClassDate.strftime(DATE_FORMAT), _align="left")),
                           TR(
                               TH(T('Time'), _align="right"),
                               TD(cls.cls.Starttime.strftime(TIME_FORMAT),
                                  ' - ',
                                  cls.cls.Endtime.strftime(TIME_FORMAT),
                                  _align="left")),
                           TR(TH(T('Location'), _align="right"),
                              TD(cls.get_location_name(), _align="left")),
                           TR(TH(T('Class'), _align="right"),
                              TD(cls.get_classtype_name(), _align="left")),
                           _cellspacing="0",
                           _cellpadding='5px',
                           _width='100%',
                           border="0")

        description = class_info
        content = XML(template_content.format(teacher_name=teacher_name, ))

        return dict(error=error,
                    error_msg=error_msg,
                    content=content,
                    description=description)
Exemple #5
0
def class_cancel_confirm():
    """
        Ask user for confirmation about really cancelling booking for a class
    """
    from openstudio.os_class_attendance import ClassAttendance
    from openstudio.os_class import Class

    clattID = request.vars['clattID']

    clatt = ClassAttendance(clattID)
    if not clatt.row.auth_customer_id == auth.user.id:
        return T('This class is not yours')

    response.title = T('Confirmation')
    response.subtitle = T('Cancel class booking?')
    response.view = 'profile/index.html'

    cls = Class(clatt.row.classes_id, clatt.row.ClassDate)
    name = XML(cls.get_name_shop())

    yes = A(T('Yes'),
            _href=URL('class_cancel', vars={'clattID':clattID}),
            _class='btn btn-primary')

    return_url = class_cancel_get_return_url()

    no = A(T('No'),
           _href=return_url,
           _class='btn btn-default')

    if clatt.get_cancellation_possible():
        buttons = DIV(yes, no, _class='')
    else:
        hours_limit = get_sys_property('shop_classes_cancellation_limit')
        buttons = DIV(B(T("We're sorry to inform you that this booking can no longer be cancelled.")), BR(),
                      SPAN(T('We accept cancellations until'), ' ', hours_limit, ' ',
                           T('hours before the start of a class.'),
                           _class='grey'),
                      BR(),
                      A(T('Back'), _href=URL('profile', 'classes'), _class='btn btn-link'))

    content = DIV(DIV(DIV(H3(T('Are you sure you want to cancel your booking for the following class?')),
                       H4(name), BR(),
                       buttons),
                      _class='box-body'),
                  _class='box box-primary center')

    back = os_gui.get_button('back', return_url)

    return dict(content=content, back=back)
Exemple #6
0
def get_class_teacher_payment():
    """

    :return:
    """
    from openstudio.os_class import Class

    set_headers()

    clsID = request.vars['clsID']
    date_received = request.vars['date']
    date = datestr_to_python("%Y-%m-%d", date_received)

    cls = Class(clsID, date)

    return dict(payment=cls.get_teacher_payment())
    def _render_email_class_info_mail(self, clattID):
        """
        :param template_content: Mail content
        :param workshops_products_id: db.workshops_products.id
        :return: mail body for workshop
        """
        from .os_class_attendance import ClassAttendance
        from .os_class import Class
        from .os_customer import Customer

        db = current.db
        T = current.T
        DATE_FORMAT = current.DATE_FORMAT
        TIME_FORMAT = current.TIME_FORMAT
        clatt = ClassAttendance(clattID)
        clsID = clatt.row.classes_id
        date = clatt.row.ClassDate
        cls = Class(clsID, date)
        customer = Customer(clatt.row.auth_customer_id)

        description = TABLE(TR(TH(T('Date')),
                               TD(clatt.row.ClassDate.strftime(DATE_FORMAT), _align="left")),
                            TR(TH(T('Time')),
                               TD(cls.get_starttime(), _aligh="left")),
                            TR(TH(T('Class')),
                               TD(cls.get_classtype_name(), _aligh="left")),
                            TR(TH(T('Location')),
                               TD(cls.get_location_name(), _aligh="left")),
                            _cellspacing="0", _cellpadding='5px', _width='100%', border="0")

        content = ''
        class_otc_mail = db.classes_otc_mail(classes_id=clsID, ClassDate=date)
        class_mail = db.classes_mail(classes_id=clsID)
        if class_otc_mail:
            content = class_otc_mail.MailContent or ""
        elif class_mail:
            content = class_mail.MailContent or ""

        return dict(
            content=DIV(
                XML(content.format(
                    customer_first_name = customer.row.first_name
                ))
            ),
            description=description
        )
Exemple #8
0
    def _render_email_template_teacher_sub_offer(self, template_content,
                                                 classes_otc_sub_avail_id):
        """

        :param classes_otc_sub_avail_id:
        :return: mail body for declined class sub request
        """
        from openstudio.os_class import Class
        from openstudio.os_teacher import Teacher

        db = current.db
        T = current.T
        DATE_FORMAT = current.DATE_FORMAT
        TIME_FORMAT = current.TIME_FORMAT

        cotcsa = db.classes_otc_sub_avail(classes_otc_sub_avail_id)
        teacher = Teacher(cotcsa.auth_teacher_id)
        cotc = db.classes_otc(cotcsa.classes_otc_id)
        cls = Class(cotc.classes_id, cotc.ClassDate)

        class_info = TABLE(TR(
            TH(T('Date'), _align="right"),
            TD(cotc.ClassDate.strftime(DATE_FORMAT), _align="left")),
                           TR(
                               TH(T('Time'), _align="right"),
                               TD(cls.cls.Starttime.strftime(TIME_FORMAT),
                                  ' - ',
                                  cls.cls.Endtime.strftime(TIME_FORMAT),
                                  _align="left")),
                           TR(TH(T('Location'), _align="right"),
                              TD(cls.get_location_name(), _align="left")),
                           TR(TH(T('Class'), _align="right"),
                              TD(cls.get_classtype_name(), _align="left")),
                           _cellspacing="0",
                           _cellpadding='5px',
                           _width='100%',
                           border="0")

        content = XML(
            template_content.format(teacher_name=teacher.get_first_name()))

        return dict(content=content, description=class_info)
Exemple #9
0
    def _render_email_template_teacher_sub_requests_daily_summary(
            self, template_content, auth_user_id):
        """
        :param template_content:
        :param auth_user_id:
        :return:
        """
        from openstudio.os_class import Class
        from openstudio.os_teacher import Teacher
        from openstudio.tools import OsTools
        from openstudio.os_classes_otcs import ClassesOTCs

        db = current.db
        T = current.T
        os_tools = OsTools()
        cotcs = ClassesOTCs()
        teacher = Teacher(auth_user_id)
        DATE_FORMAT = current.DATE_FORMAT
        TIME_FORMAT = current.TIME_FORMAT
        error = False
        error_msg = ''

        date_from = current.TODAY_LOCAL + datetime.timedelta(days=1)
        date_until = date_from + datetime.timedelta(days=45)

        # G get list of allowed class types
        query = (db.teachers_classtypes.auth_user_id == auth_user_id)
        classtype_rows = db(query).select(db.teachers_classtypes.ALL)
        ct_ids = []
        for row in classtype_rows:
            ct_ids.append(int(row.school_classtypes_id))

        open_classes_for_teacher = 0
        open_classes = ''
        description = ''
        if ct_ids:
            sys_hostname = os_tools.get_sys_property('sys_hostname')
            description = XML(
                template_content.format(teacher_name=teacher.get_first_name(),
                                        link_employee_portal=URL(
                                            'ep',
                                            'index',
                                            scheme='https',
                                            host=sys_hostname)))

            open_classes = TABLE(
                THEAD(
                    TR(
                        TH(T("Date"), _align="left"),
                        TH(T("Time"), _align="left"),
                        TH(T("Location"), _align="left"),
                        TH(T("Class"), _align="left"),
                        # TH(),
                    )),
                _cellspacing="0",
                _cellpadding='5px',
                _width='100%',
                border="0")

            # Get Open classes in the next 45 days
            rows = cotcs.get_sub_teacher_rows(date_from,
                                              date_until,
                                              school_classtypes_ids=ct_ids,
                                              only_open=True)

            for i, row in enumerate(rows):
                repr_row = list(rows[i:i + 1].render())[0]

                date = row.classes_otc.ClassDate
                clsID = row.classes.id
                cls = Class(clsID, date)
                regular_teachers = cls.get_regular_teacher_ids()

                if regular_teachers['auth_teacher_id'] == auth_user_id:
                    continue

                open_classes.append(
                    TR(
                        TD(repr_row.classes_otc.ClassDate, _align="left"),
                        TD(repr_row.classes.Starttime, _align="left"),
                        TD(repr_row.classes.school_locations_id,
                           _align="left"),
                        TD(repr_row.classes.school_classtypes_id,
                           _align="left"),
                        # TD('Actions here?'),
                    ))

                open_classes_for_teacher += 1

        if not open_classes_for_teacher:
            error = True
            error_msg = T(
                "No upcoming classes with subs required found for this teacher"
            )

        return dict(content=open_classes,
                    description=description,
                    error=error,
                    error_msg=error_msg)
Exemple #10
0
def checkin():
    """
        Checkin page for self checkin
    """
    clsID = request.vars['clsID']
    date_formatted = request.vars['date']
    date = datestr_to_python(DATE_FORMAT, date_formatted)

    session.customers_load_list_search_name = None
    session.classes_attendance_signin_back = 'self_checkin'

    cls = Class(clsID, date)
    starttime = cls.cls.Starttime.strftime(TIME_FORMAT)
    pretty_date = date.strftime('%B %d, %Y')
    classtype = db.school_classtypes(cls.cls.school_classtypes_id)
    location = db.school_locations(cls.cls.school_locations_id)

    response.title = T(classtype.Name)
    response.subtitle = SPAN(starttime, ' ', pretty_date)
    response.logout = A(SPAN(_class='glyphicon glyphicon-chevron-left'),
                        _href=URL('classes',
                                  vars={'locID': cls.cls.school_locations_id}))

    #response.view = 'templates/selfcheckin/checkin.html'

    # Check if the class is full
    full = cls.get_full()

    message = ''
    if full:
        message = DIV(
            BR(),
            os_gui.get_alert(
                'info',
                SPAN(
                    B(T('Notice')), BR(),
                    T("This class is full, it's no longer possible to check in"
                      )),
                dismissable=False))

    search_results = DIV(LOAD(
        'customers',
        'load_list.load',
        target='attendance_list_customers_list',
        content=os_gui.get_ajax_loader(message=T("Searching...")),
        vars={
            'list_type': 'selfcheckin_checkin',
            'items_per_page': 10,
            'clsID': clsID,
            'date': date_formatted,
            'pictures': False
        },
        ajax=True),
                         _id="attendance_list_customers_list",
                         _class="load_list_customers clear")

    name = ''

    show_subscriptions_prop = 'selfcheckin_show_subscriptions'
    show_subscriptions = get_sys_property(show_subscriptions_prop)
    if show_subscriptions:
        show_subscriptions = True
    else:
        show_subscriptions = False

    ah = AttendanceHelper()
    customers = ah.get_checkin_list_customers_booked(
        clsID,
        date,
        pictures=False,
        invoices=False,
        show_notes=False,
        reservations=False,
        manage_checkin=False,
        show_booking_time=False,
        show_subscriptions=show_subscriptions,
        class_full=full)
    form = checkin_get_search_form(clsID, date, name, request.function)

    content = DIV(
        SPAN(location.Name, _class='grey'),
        message,
        customers,
    )

    if not full:
        content.append(
            DIV(H2(T("I'm not yet on the list...")), form, search_results))

    return dict(content=content)
    def render_email_template(self,
                              email_template,
                              title='',
                              subject='',
                              description='',
                              comments='',
                              template_content=None,
                              auth_user_id=None,
                              customers_orders_id=None,
                              customer_subscriptions_id=None,
                              customers_classcards_id=None,
                              invoices_id=None,
                              invoices_payments_id=None,
                              classes_attendance_id=None,
                              classes_otc_id=None,
                              classes_otc_sub_avail_id=None,
                              workshops_products_customers_id=None,
                              return_html=False):
        """
            Renders default email template
            uses the render function from gluon.template instead of response.render
            response throws a RestrictedError when run from the scheduler or shell...
            and we do want scheduled emails to be rendered :)
        """
        # from gluon.template import parse_template
        from gluon.template import render

        db = current.db
        T = current.T
        DATETIME_FORMAT = current.DATETIME_FORMAT
        error = False
        error_msg = ''

        request = current.request

        logo = self._render_email_template_get_logo()

        template_name = 'default.html'
        template_path = os.path.join(request.folder, 'views', 'templates', 'email')
        # Get template
        if template_content is None:
            # Get email template from db
            template_content = self.get_email_template(email_template)

        # Render template
        if email_template == 'order_received':
            subject = T('Order received')
            # do some pre-processing to show the correct order info
            content = self._render_email_template_order(template_content, customers_orders_id)

            # Check for order message
            from .os_order import Order
            order = Order(customers_orders_id)
            if order.order.CustomerNote:
                comments = DIV(
                    T("We received the following message with your order:"), BR(), BR(),
                    XML(order.order.CustomerNote.replace('\n', '<br>'))
                )

        elif email_template == 'order_delivered':
            subject = T('Order delivered')
            # do some pre-processing to show the correct order info
            content = self._render_email_template_order(template_content, customers_orders_id)

        elif email_template == 'payment_recurring_failed':
            subject = T('Recurring payment failed')
            content = self._render_email_template_payment_recurring_failed(template_content)

        elif email_template == "subscription_created":
            subject = T("Subscription activated")
            result = self._render_email_template_subscription_created(
                template_content,
                customer_subscriptions_id
            )
            title = T("Your subscription has been activated!")
            description = result['description']
            content = result['content']

        elif email_template == 'teacher_sub_requests_daily_summary':
            result = self._render_email_template_teacher_sub_requests_daily_summary(
                template_content,
                auth_user_id
            )
            title = T("Daily summary - open classes")
            description = result['description']
            content = result['content']
            error = result['error']
            error_msg = result['error_msg']

        elif email_template == 'teacher_sub_request_open_reminder':
            result = self._render_email_template_teacher_sub_request_open_reminder(
                template_content,
                classes_otc_id
            )
            title = T("A friendly reminder")
            description = result['description']
            content = result['content']
            error = result['error']
            error_msg = result['error_msg']

        elif email_template == 'teacher_sub_offer_declined':
            result = self._render_email_template_teacher_sub_offer(
                template_content,
                classes_otc_sub_avail_id
            )
            title = T("Substitute offer declined")
            description = result['description']
            content = result['content']

        elif email_template == 'teacher_sub_offer_accepted':
            result = self._render_email_template_teacher_sub_offer(
                template_content,
                classes_otc_sub_avail_id
            )
            title = T("Thank you for teaching this class")
            description = result['description']
            content = result['content']

        elif email_template == "trial_follow_up":
            result = self._render_email_trial_follow_up(
                template_content,
                classes_attendance_id=classes_attendance_id,
                customers_classcards_id=customers_classcards_id,
            )

            subject = T("Trial follow up")
            content = result['content']

        elif email_template == 'classes_info_mail':
            from .os_class import Class
            from .os_class_attendance import ClassAttendance
            clatt = ClassAttendance(classes_attendance_id)
            cls = Class(clatt.row.classes_id, clatt.row.ClassDate)
            class_name = cls.get_name()

            subject = T("Class booking") + " " + class_name
            title = T("We've reserved your spot!")
            result = self._render_email_class_info_mail(classes_attendance_id)
            content = result['content']
            description = result['description']

        elif email_template == 'workshops_info_mail':
            wspc = db.workshops_products_customers(workshops_products_customers_id)
            wsp = db.workshops_products(wspc.workshops_products_id)
            ws = db.workshops(wsp.workshops_id)
            subject = ws.Name
            title = ws.Name
            result = self._render_email_workshops_info_mail(wspc, wsp, ws)
            content = result['content']
            description = result['description']
            
        elif (email_template == 'sys_verify_email' or
              email_template == 'sys_reset_password'):
            template_name = 'default_simple.html'
            content = XML(template_content)
            subject = subject

        else:
            template_name = 'default.html'
            content = XML(template_content)
            subject = subject

        footer = XML(self.get_email_template('sys_email_footer'))

        template = os.path.join(
            template_path,
            template_name
        )

        context = dict(
            logo = logo,
            title = title,
            description = description,
            content = content,
            comments = comments,
            footer = footer,
            request = request
        )

        html_message = render(
            filename = template,
            path = template_path,
            context = context
        )

        if return_html:
            return dict(
                msg_subject = subject or "",
                html_message = html_message,
                error = error,
                error_msg = error_msg
            )
        else:
            msgID = db.messages.insert(
                msg_content = html_message,
                msg_subject = subject
            )

            return msgID
Exemple #12
0
def edit():
    """
        :return: shows order
    """
    from openstudio.os_class import Class

    response.title = T('Order #') + request.vars['coID']
    response.subtitle = T('Edit')
    response.view = 'general/only_content.html'

    coID = request.vars['coID']

    order = Order(coID)
    cuID = order.order.auth_customer_id
    customer = Customer(cuID)
    # Info table
    info = TABLE(THEAD(TR(TH(T('Customer')),
                          TH(T('Ordered on')),
                          TH(T('Status')),
                          TH(),
                          )),
                 _class='table')

    # Display status
    for field in db.customers_orders:
        field.readable = False
        field.writable = False

    db.customers_orders.Status.readable = True
    db.customers_orders.Status.writable = True

    crud.messages.record_updated = T('Saved')
    form = crud.update(db.customers_orders, coID)

    result = set_form_id_and_get_submit_button(form, 'MainForm')
    form = result['form']
    submit = result['submit']

    form = DIV(XML('<form id="MainForm" action="#" enctype="multipart/form-data" method="post">'),
               form.custom.widget.Status,
               form.custom.end)

    #status = form

    # status = represent_customers_orders_status(order.order.Status, order.order)
    # Display ordered on
    ordered_on = represent_datetime(order.order.DateCreated, order.order)
    customer_link = A(customer.get_name(),
                      _href=URL('customers', 'edit', args=customer.row.id))
    info.append(TR(
        TD(customer_link),
        TD(ordered_on),
        TD(form),
        TD()
    ))

    # Info content
    content = DIV(DIV(info, _class='col-md-8 no-padding-left'))

    # Display items
    rows = order.get_order_items_rows()

    header = THEAD(TR(TH(T('Product')),
                      TH(T('Description')),
                      TH(SPAN(T('Amount incl. VAT'), _class='right')),
                      TH(T("G/L Account")),
                      TH(T("Cost center")),
                      TH(T("Class check-in")),
                      TH(),
                        )
                     )
    table = TABLE(header, _class='table table-striped table-hover order-items')


    for i, row in enumerate(rows):
        repr_row = list(rows[i:i + 1].render())[0]

        class_name = ""
        if row.customers_orders_items.classes_id and row.customers_orders_items.ClassDate:
            cls = Class(row.customers_orders_items.classes_id,
                        row.customers_orders_items.ClassDate)
            class_name = cls.get_name()


        table.append(TR(
            TD(repr_row.customers_orders_items.ProductName),
            TD(repr_row.customers_orders_items.Description),
            TD(SPAN(repr_row.customers_orders_items.TotalPriceVAT, _class='right')),
            TD(repr_row.customers_orders_items.accounting_glaccounts_id),
            TD(repr_row.customers_orders_items.accounting_costcenters_id),
            TD(class_name),
            TD(),
        ))

    # Display totals
    amounts = order.get_amounts()

    footer = TFOOT(TR(TD(),
                    TD(B(T('Subtotal'))),
                    TD(SPAN(CURRSYM, ' ', format(amounts.TotalPrice, '.2f'), _class='bold right')),
                    TD()
                    ),
                  TR(TD(),
                    TD(B(T('VAT'))),
                    TD(SPAN(CURRSYM, ' ', format(amounts.VAT, '.2f'), _class='bold right')),
                    TD()
                    ),
                  TR(TD(),
                    TD(B(T('Total'))),
                    TD(SPAN(CURRSYM, ' ', format(amounts.TotalPriceVAT, '.2f'), _class='bold right')),
                    TD()
                    ))
    table.append(footer)

    content.append(table)

    # Customer message
    customer_message = ''
    if order.order.CustomerNote:
        customer_message = DIV(
            B(T('Customer message')), BR(), BR(),
            XML(order.order.CustomerNote.replace("\n", "<br>")),
        )
    content.append(customer_message)

    back = os_gui.get_button('back', edit_get_return_url(cuID))

    return dict(content=content, back=back, save=submit)