Search code examples
ruby-on-railsrubyzohozohobooks

Create purchase order in zoho books via API in Ruby on Rails


I am using the Zoho Books API to create a purchase order

(https://www.zoho.com/books/api/v3/#Purchase-Order_Create_a_purchase_order) -

The only required items appear to be vendor_id and line_items. However, after creating a job I am getting following message:

ZohoBooks::BadRequestError: Purchase order cannot be created for a non-purchase item.

And here is the code:

```

class CreatePurchaseOrderJob < ApplicationJob
  queue_as :default

  def perform(order_id)
    @order = SupplierOrder.find(order_id)
    create_purchase_order
  end

  private

  attr_reader :order

  def vendor
    name = order.supplier.name
    books.get_contact_with_options(contact_type: :vendor, contact_name: name)
  end

  def create_purchase_order
    payload = {
      vendor_id: vendor['contact_id'],
      purchaseorder_number: order.reference,
      reference_number: order.reference,
      line_items: line_items
    }

    books.create_purchase_order(payload)
  end

  def line_items
    order.rfq_line_item_prices.order(:id).map do |price|
      line_item(price)
    end
  end

  def line_item(price)
    {
      name: price.current_line_item.shape,
      description: price.current_line_item&.name,
      bcy_rate: price.unit_price.to_f,
      rate: price.unit_price.to_f,
      quantity: price.current_line_item.quantity,
      tax_id: Registry.zoho_vat_tax_id,
      item_custom_fields: [
        { label: 'Grade', value: price.current_line_item&.grade },
        { label: 'Finish', value: price.current_line_item&.finish },
        { label: 'Dimensions', value: price.current_line_item&.dimensions }
      ]
    }
  end

  def books
    @books ||= Registry.books
  end
end

This is the same code we use to make invoices and that works, so I'm missing something magical that let's Zoho know this is a purchase item, without any indication to what that may be. I asked via chat and got the following: The reason why you're getting this error message is because, You can create a purchase order only when you add the purchase info for an item.


Solution

  • So the answer turned out to be easier than I thought, although it took longer than I would have liked to uncover. Eventually I decided try adding attributes even if they were defined as optional. By including the item id it allowed the purchase order to be created.

    My final code:

    class CreatePurchaseOrderJob < ApplicationJob
      queue_as :default
    
      def perform(order_id)
        @order = SupplierOrder.find(order_id)
        create_purchase_order
      end
    
      private
    
      def payload
        {
          vendor_id: vendor['contact_id'],
          reference_number: order&.reference,
          line_items: line_items
        }
      end
    
      attr_reader :order
    
      def vendor
        @vendor ||= books.find_or_create_vendor(order.supplier)
      end
    
      def create_purchase_order
        begin
          books.create_purchase_order(payload)
        rescue Exception => e
          Airbrake.notify(e)
        end
      end
    
      def line_items
        order.line_item_prices.order(:id).map do |price|
          line_item(price)
        end
      end
    
      def item_id(name)
        books.find_or_create_item(name)['item_id']
      end
    
      def line_item(price)
        {
          item_id: item_id(price.current_line_item.shape),
          description: price.current_line_item&.description,
          bcy_rate: price.unit_price.to_f,
          rate: price.unit_price.to_f,
          quantity: price.current_line_item.quantity,
          tax_id: Registry.vat_tax_id,
          item_custom_fields: [
            { label: 'Grade', value: price.current_line_item&.grade },
            { label: 'Finish', value: price.current_line_item&.finish },
            { label: 'Dimensions', value: price.current_line_item&.dimensions }
          ]
        }
      end
    
      def books
        @books ||= Registry.books
      end
    end