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 Reading

Implementing Carousel Slider in PSLab Android App

This blog is a demonstration for creating a Carousel Picker in Android by taking an example of the Carousel Picker made in PSLab Android app under PR #1007. Some improvement to this would be to add custom animation to the ViewPager and adjusting the ViewPager sliding speed. So first let’s start with the basics and terminology of Carousel.

What is Carousel?

Carousel according to the dictionary means roundabout or merry-go-round. The term was mainly used for the traditional amusement ride of a merry-go-round in amusement parks with seats of horses. The same is the working of Carousel View in Android. It gives a smooth sliding effect to slide between a variety of options available.

How to implement Carousel View in the app?

Following are the steps to implement a basic Carousel View in the app. Further effects and upgrades can be given as per the need.

  • The first step is to add jitpack to your app’s gradle file
maven { url 'https://jitpack.io '}
  • Now add a library dependency in your project level gradle file
compile 'com.github.Vatican-Cameos:CarouselPicker:v1.0

The above dependency uses the View Pager and Gesture Detector functionality provided by Android. The Gesture Detector class detects the swipe gesture made by the user and the View Pager highlights the relevant label in the Carousel box according to the swipe done i.e left or right.

  • Now Carousel Picker is ready to be added directly to layouts. So, add the Carousel by adding the following layout code at a proper section in layouts file.
<in.goodiebag.carouselpicker.CarouselPicker
	android:id="@+id/carouselPicker"
	android:layout_width="match_parent"
	android:layout_height="wrap_content"
	android:layout_marginTop="20dp"
	android:layout_marginBottom="20dp"
	android:background="#DDD"
	apps:items_visible="three" />

Here, the items_visible is used to provide the Carousel Picker with the number of max items to be seen at a time on screen where only one item will be in focus. Other items are adjusted on the side and can be viewed by scrolling.

  • Now as we have implemented the layouts, so now’s the time to set adapter and resource type for Carousel to hold in Java files. First, find the Carousel View with its id.
CarouselPicker carouselPicker = findViewById(R.id.carouselPicker);
  • Now set a list of items to be added in the Carousel Picker. The items can be both images and texts.
List<CarouselPicker.PickerItem> items = new ArrayList<>();

To add images :

items.add(new CarouselPicker.DrawableItem(R.mipmap.ic_launcher));

To add texts/strings :

items.add(new CarouselPicker.TextItem("Example", 10));

Here, the integer that is added after the text indicates the size in sp of the text that is to be displayed in Carousel View.

  • Now after creating a list of items, make an adapter which provides this list of information to Carousel Picker.
CarouselPicker.CarouselViewAdpater adapter = new CarouselPicker.CarouselViewAdpater(this, items);
  • Now set the adapter for the Carousel View :
carouselPicker.setAdapter(adapter);
  • To dynamically add items to the Carousel View, simply change the list of items in the list provided to the adapter and then use
adapter.notifyDataSetChanged();
  • Now to change the functionality of the app with every Carousel item, implement the onPageChangeListener as Carousel View implements ViewPager class.
carouselPicker.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        	@Override
        	public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        	}

        	@Override
        	public void onPageSelected(int position) {
        	}

        	@Override
        	public void onPageScrollStateChanged(int state) {
          }

Following GIF shows how Carousel View looked after implementation in PSLab app. Each option provided in the view was used to provide user with a different channel selection mode.

Figure 1. GIF of implemented Carousel View in PSLab app

So in this way, a Carousel Picker or Carousel View can be implemented in the app. Further functionalities of animations, mirroring, shadow effect, all can be done with just minor changes in the above code. And to fully customize the look of the Carousel or to enable infinite scrolling feature, a local Carousel Picker can be implemented by just making a custom adapter and a class that extends ViewPager class. Below are the resources to implement both custom and dependency based Carousel View.

Resources

  1. https://www.youtube.com/watch?v=sTJm1Ys9jMI – Youtube Video for dependency based Carousel View
  2. https://www.youtube.com/watch?v=4ct0oPf_u2o – Youtube Video for implementing infinite scrolling
  3. http://www.codexpedia.com/android/android-carousel-view-using-viewpager/ – An article to implement custom Carousel View

 

 

 

 

 

Continue Reading
Close Menu