Skip to content

Commit

Permalink
Add DiscountRate to line items (#121)
Browse files Browse the repository at this point in the history
  • Loading branch information
armstrjare authored Jan 24, 2018
1 parent f1cefe4 commit 230265f
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 22 deletions.
46 changes: 25 additions & 21 deletions lib/xero_gateway/line_item.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,73 +3,75 @@
module XeroGateway
class LineItem
include Money

TAX_TYPE = Account::TAX_TYPE unless defined?(TAX_TYPE)

# Any errors that occurred when the #valid? method called.
attr_reader :errors

# All accessible fields
attr_accessor :line_item_id, :description, :quantity, :unit_amount, :item_code, :tax_type, :tax_amount, :account_code, :tracking
attr_accessor :line_item_id, :description, :quantity, :unit_amount, :discount_rate, :item_code, :tax_type, :tax_amount, :account_code, :tracking

def initialize(params = {})
@errors ||= []
@tracking ||= []
@quantity = 1
@unit_amount = BigDecimal.new('0')

params.each do |k,v|
self.send("#{k}=", v)
end
end

# Validate the LineItem record according to what will be valid by the gateway.
#
# Usage:
# Usage:
# line_item.valid? # Returns true/false
#
#
# Additionally sets line_item.errors array to an array of field/error.
def valid?
@errors = []

if !line_item_id.nil? && line_item_id !~ GUID_REGEX
@errors << ['line_item_id', 'must be blank or a valid Xero GUID']
end

unless description
@errors << ['description', "can't be blank"]
end

if tax_type && !TAX_TYPE[tax_type]
@errors << ['tax_type', "must be one of #{TAX_TYPE.keys.join('/')}"]
end

@errors.size == 0
end

def has_tracking?
return false if tracking.nil?

if tracking.is_a?(Array)
return tracking.any?
else
return tracking.is_a?(TrackingCategory)
end
end

# Deprecated (but API for setter remains).
#
# As line_amount must equal quantity * unit_amount for the API call to pass, this is now
# automatically calculated in the line_amount method.
def line_amount=(value)
end

# Calculate the line_amount as quantity * unit_amount as this value must be correct
# for the API call to succeed.
def line_amount
quantity * unit_amount
total = quantity * unit_amount
total = total * (1 - (discount_rate / BigDecimal.new(100))) if discount_rate
total
end

def to_xml(b = Builder::XmlMarkup.new)
b.LineItem {
b.Description description
Expand All @@ -79,6 +81,7 @@ def to_xml(b = Builder::XmlMarkup.new)
b.TaxType tax_type if tax_type
b.TaxAmount tax_amount if tax_amount
b.LineAmount line_amount if line_amount
b.DiscountRate discount_rate if discount_rate
b.AccountCode account_code if account_code
if has_tracking?
b.Tracking {
Expand All @@ -92,7 +95,7 @@ def to_xml(b = Builder::XmlMarkup.new)
end
}
end

def self.from_xml(line_item_element)
line_item = LineItem.new
line_item_element.children.each do |element|
Expand All @@ -105,6 +108,7 @@ def self.from_xml(line_item_element)
when "TaxType" then line_item.tax_type = element.text
when "TaxAmount" then line_item.tax_amount = BigDecimal.new(element.text)
when "LineAmount" then line_item.line_amount = BigDecimal.new(element.text)
when "DiscountRate" then line_item.discount_rate = BigDecimal.new(element.text)
when "AccountCode" then line_item.account_code = element.text
when "Tracking" then
element.children.each do | tracking_element |
Expand All @@ -113,13 +117,13 @@ def self.from_xml(line_item_element)
end
end
line_item
end
end

def ==(other)
[:description, :quantity, :unit_amount, :tax_type, :tax_amount, :line_amount, :account_code, :item_code].each do |field|
[:description, :quantity, :unit_amount, :tax_type, :tax_amount, :line_amount, :discount_rate, :account_code, :item_code].each do |field|
return false if send(field) != other.send(field)
end
return true
end
end
end
end
34 changes: 33 additions & 1 deletion test/unit/invoice_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,18 @@ class InvoiceTest < Test::Unit::TestCase
parsed_invoice = XeroGateway::Invoice.from_xml(invoice_element)
assert_equal 'http://example.com?with=params&and=more', parsed_invoice.url
end

should "work with line_item discount rates" do
invoice = create_test_invoice
invoice.line_items.first.discount_rate = 27
invoice_as_xml = invoice.to_xml

invoice_element = REXML::XPath.first(REXML::Document.new(invoice_as_xml), "/Invoice")
result_invoice = XeroGateway::Invoice.from_xml(invoice_element)

assert_equal(invoice, result_invoice)
assert_equal 27, result_invoice.line_items.first.discount_rate
end
end

# Tests the sub_total calculation and that setting it manually doesn't modify the data.
Expand Down Expand Up @@ -154,6 +166,26 @@ def test_line_amount_calculation
assert_equal(quantity * line_item.unit_amount, line_item.line_amount)
end

def test_line_amount_discount_calculation
invoice = create_test_invoice
line_item = invoice.line_items.first
line_item.discount_rate = 12.5

# Make sure that everything adds up to begin with.
expected_amount = line_item.quantity * line_item.unit_amount * 0.875
assert_equal(expected_amount, line_item.line_amount)

# Change the line_amount and check that it doesn't modify anything.
line_item.line_amount = expected_amount * 10
assert_equal(expected_amount, line_item.line_amount)

# Change the quantity and check that the line_amount has been updated.
quantity = line_item.quantity + 2
line_item.quantity = quantity
assert_not_equal(expected_amount, line_item.line_amount)
assert_equal(quantity * line_item.unit_amount * 0.875, line_item.line_amount)
end

# Ensure that the totalling methods don't raise exceptions, even when
# invoice.line_items is empty.
def test_totalling_methods_when_line_items_empty
Expand Down Expand Up @@ -286,7 +318,7 @@ def test_instantiate_invoice_with_default_line_amount_types
def test_optional_params
eur_code = "EUR"
eur_rate = 1.80

invoice = create_test_invoice(:url => 'http://example.com', :branding_theme_id => 'a94a78db-5cc6-4e26-a52b-045237e56e6e', :currency_code => eur_code, :currency_rate => eur_rate)
assert_equal 'http://example.com', invoice.url
assert_equal 'a94a78db-5cc6-4e26-a52b-045237e56e6e', invoice.branding_theme_id
Expand Down

0 comments on commit 230265f

Please sign in to comment.