Designing and optimising new invoice PDFs

The Open Event project has proven to be an excellent event management application with a growing user base. With recent workflow refactors in the order process in open-event-frontend and introduction of event invoices (to be rolled out this month as a work product), the open-event-server’s invoices required a makeover. A ticket buyer is now required to give their billing information if the order is comprised of paid tickets and to accommodate this, and long information addresses, optimisation was required.

Restructuring order invoices

The new order invoices use nested tables concept instead of previously used two-cell tables. The pros of this new design is the accomodation of long-addresses and corresponding changes in billing information display.

{% if order.is_billing_enabled %}
                      <
td style="text-align:center;">
                          <
table>
                              <
tr>
                                  <
td>
                                      <
strong>Company :</strong>
                                  </
td>
                                  <
td>
                                      <
strong>{{ order.company }}</strong>
                                  </
td>
                              </
tr>
                              <
tr>
                                  <
td valign="top">
                                      <
strong>Tax Info :</strong>
                                  </
td>
                                  <
td>
                                      <
strong>{{ order.tax_business_info }}</strong>
                                  </
td>
                              </
tr>
                              <
tr>
                                  <
td valign="top">
                                      <
strong>Address :</strong>
                                  </
td>
                                  <
td>
                                      <
strong>{{ order.address }}</strong>
                                  </
td>
                              </
tr>
                              <
tr>
                                  <
td>
                                      <
strong>City :</strong>
                                  </
td>
                                  <
td>
                                      <
strong>{{ order.city }}</strong>
                                  </
td>
                              </
tr>
                              <
tr>
                                  <
td>
                                      <
strong>State :</strong>
                                  </
td>
                                  <
td>
                                      <
strong>{{ order.state }}</strong>
                                  </
td>
                              </
tr>
                              <
tr>
                                  <
td>
                                      <
strong>Zipcode :</strong>
                                  </
td>
                                  <
td>
                                      <
strong>{{ order.zipcode }}</strong>
                                  </
td>
                              </
tr>
                              <
tr>
                                  <
td>
                                      <
strong>Country :</strong>
                                  </
td>
                                  <
td>
                                      <
strong>{{ order.country }}</strong>
                                  </
td>
                              </
tr>
                          </
table>
                      </
td>
                      {% endif %}

This made sure that the new orders have enough space to prevent information overflow and still maintain the original structure to give a sense of uniformity in old and new PDFs.

Generating new event invoices

The new event invoices needed an overall change in structure. They will be rolling out on 1st of every month, according to current implementation. This required an overall implementation of new invoices. 

First, the published events are taken in consideration for generation of invoices for a particular user. It has been implemented as a scheduled job accordingly.

events = Event.query.filter_by(deleted_at=None, state='published').all()
      for event in events:
          # calculate net & gross revenues
          user = event.owner
          admin_info = get_settings()
          currency = event.payment_currency
          ticket_fee_object = db.session.query(TicketFees).filter_by(currency=currency).one()
          ticket_fee_percentage = ticket_fee_object.service_fee
          ticket_fee_maximum = ticket_fee_object.maximum_fee
          orders = Order.query.filter_by(event=event).all()
          gross_revenue = event.calc_monthly_revenue()
          ticket_fees = event.tickets_sold * (ticket_fee_percentage / 100)
          if ticket_fees > ticket_fee_maximum:
              ticket_fees = ticket_fee_maximum
          net_revenue = gross_revenue - ticket_fees
          payment_details = {
              'tickets_sold': event.tickets_sold,
              'gross_revenue': gross_revenue,
              'net_revenue': net_revenue,
              'amount_payable': ticket_fees
          }
          # save invoice as pdf
          pdf = create_save_pdf(render_template('pdf/event_invoice.html', orders=orders, user=user,
                                admin_info=admin_info, currency=currency, event=event,
                                ticket_fee_object=ticket_fee_object, payment_details=payment_details,
                                net_revenue=net_revenue), UPLOAD_PATHS['pdf']['event_invoice'],
                                dir_path='/static/uploads/pdf/event_invoices/', identifier=event.identifier)
          # save event_invoice info to DB

          event_invoice = EventInvoice(amount=net_revenue, invoice_pdf_url=pdf, event_id=event.id)
          save_to_db(event_invoice)

This function also required one minor modification. The function for calculating monthly revenue had to be updated as to dodge certain unseen bugs related to non completed order amount calculations hence restructuring the function as follows.

def calc_monthly_revenue(self):
      """Returns revenue of current month. Invoice sent every 1st of the month for the previous month"""
      previous_month = datetime.now().month - 1
      orders = Order.query.filter_by(event_id=self.id, status='completed').all()


      monthly_revenue = sum([o.amount for o in orders if o.completed_at and o.completed_at.month == previous_month])
      return monthly_revenue

This enabled the system to finally serve event invoice PDFs. One of whose examples are given above, With this, the open-event-server is finally able to serve event invoices accordingly which can be paid via PayPal to the Eventyay account.

Resources

Related Work and Code Repository

Continue ReadingDesigning and optimising new invoice PDFs