From 779d014c3388c320d18110a1d39919726afd38d2 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Thu, 30 May 2013 18:59:43 +1000 Subject: [PATCH 01/82] Draft of payroll timesheet model. Includes TimesheetLine (and NumberOfUnits) children. --- lib/xeroizer.rb | 5 + lib/xeroizer/models/payroll/number_of_unit.rb | 17 ++ lib/xeroizer/models/payroll/timesheet.rb | 32 ++++ lib/xeroizer/models/payroll/timesheet_line.rb | 22 +++ lib/xeroizer/payroll_application.rb | 1 + lib/xeroizer/record/payroll_array_base.rb | 17 ++ .../record/payroll_array_base_model.rb | 21 +++ lib/xeroizer/record/payroll_base.rb | 4 + .../record/record_association_helper.rb | 65 +++++++- lib/xeroizer/record/xml_helper.rb | 13 ++ test/stub_responses/payroll_employees.xml | 15 ++ ...e-07c60af4-5c86-44d9-8146-fab3733a087c.xml | 147 ++++++++++++++++++ test/test_helper.rb | 22 ++- test/unit/models/payroll/timesheet_test.rb | 33 ++++ 14 files changed, 404 insertions(+), 10 deletions(-) create mode 100644 lib/xeroizer/models/payroll/number_of_unit.rb create mode 100644 lib/xeroizer/models/payroll/timesheet.rb create mode 100644 lib/xeroizer/models/payroll/timesheet_line.rb create mode 100644 lib/xeroizer/record/payroll_array_base.rb create mode 100644 lib/xeroizer/record/payroll_array_base_model.rb create mode 100644 test/stub_responses/payroll_employees.xml create mode 100644 test/stub_responses/records/payroll_employee-07c60af4-5c86-44d9-8146-fab3733a087c.xml create mode 100644 test/unit/models/payroll/timesheet_test.rb diff --git a/lib/xeroizer.rb b/lib/xeroizer.rb index 38028396..1d314e8d 100644 --- a/lib/xeroizer.rb +++ b/lib/xeroizer.rb @@ -23,8 +23,10 @@ require 'xeroizer/record/base_model' require 'xeroizer/record/payroll_base_model' +require 'xeroizer/record/payroll_array_base_model' require 'xeroizer/record/base' require 'xeroizer/record/payroll_base' +require 'xeroizer/record/payroll_array_base' require 'xeroizer/configuration' # Include models @@ -59,6 +61,9 @@ require 'xeroizer/models/payroll/home_address' require 'xeroizer/models/payroll/bank_account' require 'xeroizer/models/payroll/employee' +require 'xeroizer/models/payroll/timesheet' +require 'xeroizer/models/payroll/timesheet_line' +require 'xeroizer/models/payroll/number_of_unit' require 'xeroizer/report/factory' diff --git a/lib/xeroizer/models/payroll/number_of_unit.rb b/lib/xeroizer/models/payroll/number_of_unit.rb new file mode 100644 index 00000000..0c54a546 --- /dev/null +++ b/lib/xeroizer/models/payroll/number_of_unit.rb @@ -0,0 +1,17 @@ +module Xeroizer + module Record + module Payroll + + class NumberOfUnitModel < PayrollArrayBaseModel + + end + + class NumberOfUnit < PayrollArrayBase + + decimal :value + + end + + end + end +end diff --git a/lib/xeroizer/models/payroll/timesheet.rb b/lib/xeroizer/models/payroll/timesheet.rb new file mode 100644 index 00000000..da161709 --- /dev/null +++ b/lib/xeroizer/models/payroll/timesheet.rb @@ -0,0 +1,32 @@ +module Xeroizer + module Record + module Payroll + + class TimesheetModel < PayrollBaseModel + + set_permissions :read, :write, :update + + end + + class Timesheet < PayrollBase + + set_primary_key :timesheet_id + + guid :timesheet_id + guid :employee_id + date :start_date + date :end_date + decimal :hours + string :status + + datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC' + + has_many :timesheet_lines + + validates_presence_of :start_date, :end_date, :employee_id + + end + + end + end +end diff --git a/lib/xeroizer/models/payroll/timesheet_line.rb b/lib/xeroizer/models/payroll/timesheet_line.rb new file mode 100644 index 00000000..2c46d612 --- /dev/null +++ b/lib/xeroizer/models/payroll/timesheet_line.rb @@ -0,0 +1,22 @@ +module Xeroizer + module Record + module Payroll + + class TimesheetLineModel < PayrollBaseModel + + end + + class TimesheetLine < PayrollBase + + guid :earnings_rate_id + guid :tracking_item_id # TODO: belongs to a http://developer.xero.com/api/Tracking%20Categories/ (optionally? the docs don't have any examples) + + has_array :number_of_units + + datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC' + + end + + end + end +end diff --git a/lib/xeroizer/payroll_application.rb b/lib/xeroizer/payroll_application.rb index 6aec0276..95bd7a18 100644 --- a/lib/xeroizer/payroll_application.rb +++ b/lib/xeroizer/payroll_application.rb @@ -19,6 +19,7 @@ def self.record(record_type) end record :Employee + record :Timesheet def initialize(application) @application = application diff --git a/lib/xeroizer/record/payroll_array_base.rb b/lib/xeroizer/record/payroll_array_base.rb new file mode 100644 index 00000000..fe54a3c8 --- /dev/null +++ b/lib/xeroizer/record/payroll_array_base.rb @@ -0,0 +1,17 @@ +module Xeroizer + module Record + + class PayrollArrayBase < PayrollBase + + class_inheritable_attributes :fields, :possible_primary_keys, :primary_key_name, :summary_only, :validators + + def self.build(value, parent) + record = new(parent) + record.value = value + record + end + + end + + end +end diff --git a/lib/xeroizer/record/payroll_array_base_model.rb b/lib/xeroizer/record/payroll_array_base_model.rb new file mode 100644 index 00000000..bf654c72 --- /dev/null +++ b/lib/xeroizer/record/payroll_array_base_model.rb @@ -0,0 +1,21 @@ +module Xeroizer + module Record + + class PayrollArrayBaseModel < PayrollBaseModel + + class_inheritable_attributes :api_controller_name + class_inheritable_attributes :permissions + class_inheritable_attributes :xml_root_name + class_inheritable_attributes :optional_xml_root_name + class_inheritable_attributes :xml_node_name + + attr_accessor :value + + def to_s + value + end + + end + + end +end diff --git a/lib/xeroizer/record/payroll_base.rb b/lib/xeroizer/record/payroll_base.rb index 58bf7046..89e5b350 100644 --- a/lib/xeroizer/record/payroll_base.rb +++ b/lib/xeroizer/record/payroll_base.rb @@ -9,6 +9,10 @@ def self.belongs_to(field_name, options = {}) super(field_name, {:base_module => Xeroizer::Record::Payroll}.merge(options)) end + def self.has_array(field_name, options = {}) + super(field_name, {:base_module => Xeroizer::Record::Payroll}.merge(options)) + end + def self.has_many(field_name, options = {}) super(field_name, {:base_module => Xeroizer::Record::Payroll}.merge(options)) end diff --git a/lib/xeroizer/record/record_association_helper.rb b/lib/xeroizer/record/record_association_helper.rb index 765e70d3..5830eaee 100644 --- a/lib/xeroizer/record/record_association_helper.rb +++ b/lib/xeroizer/record/record_association_helper.rb @@ -29,6 +29,50 @@ def belongs_to(field_name, options = {}) self.attributes[field_name] = record end end + + def has_array(field_name, options = {}) + internal_field_name = options[:internal_name] || field_name + internal_plural_field_name = options[:internal_name_plural] || internal_field_name.to_s + + define_association_attribute(field_name, internal_field_name, :has_array, options) + + define_method "add_to_#{internal_plural_field_name}" do | *args | + # The name of the record model. + model_name = options[:model_name] ? options[:model_name].to_sym : field_name.to_s.singularize.camelize.to_sym + + # The record's parent instance for this current application. + model_parent = new_model_class(model_name) + + # The class of this record. + record_class = (options[:base_module] || Xeroizer::Record).const_get(model_name) + + # Parse the *args variable so that we can use this method like: + # add_to_records([value, value, value]) + # add_to_records(value) + values = [] + if args.size == 1 && !args.first.is_a?(Array) + values = [args.first] + elsif args.size > 0 + values = args + else + raise StandardError.new("Invalid arguments for #{self.class.name}#add_#{internal_singular_field_name}(#{args.inspect}).") + end + + # Ensure that complete record is downloaded before adding new records + self.send(field_name) + + # Add each value. + last_record = nil + values.each do |value| + record = record_class.build(value, model_parent) + self.attributes[field_name] ||= [] + self.attributes[field_name] << record + last_record = record + end + + last_record + end + end def has_many(field_name, options = {}) internal_field_name = options[:internal_name] || field_name @@ -80,7 +124,7 @@ def has_many(field_name, options = {}) end def define_association_attribute(field_name, internal_field_name, association_type, options) - define_simple_attribute(field_name, association_type, options.merge!(:skip_writer => true), ((association_type == :has_many) ? [] : nil)) + define_simple_attribute(field_name, association_type, options.merge!(:skip_writer => true), value_if_nil(association_type)) internal_field_name = options[:internal_name] || field_name internal_singular_field_name = options[:internal_name_singular] || internal_field_name.to_s.singularize @@ -90,7 +134,7 @@ def define_association_attribute(field_name, internal_field_name, association_ty record_class = (options[:base_module] || Xeroizer::Record).const_get(model_name) case value when Hash - self.attributes[field_name] = ((association_type == :has_many) ? [] : nil) + self.attributes[field_name] = self.class.value_if_nil(association_type) case association_type when :has_many self.attributes[field_name] = [] @@ -102,7 +146,7 @@ def define_association_attribute(field_name, internal_field_name, association_ty end when Array - self.attributes[field_name] = ((association_type == :has_many) ? [] : nil) + self.attributes[field_name] = self.class.value_if_nil(association_type) value.each do | single_value | case single_value when Hash then send("add_#{internal_singular_field_name}".to_sym, single_value) @@ -112,7 +156,7 @@ def define_association_attribute(field_name, internal_field_name, association_ty end when record_class - self.attributes[field_name] = ((association_type == :has_many) ? [value] : value) + self.attributes[field_name] = self.class.value_if_nil(association_type, value) when NilClass self.attributes[field_name] = [] @@ -128,10 +172,21 @@ def define_association_attribute(field_name, internal_field_name, association_ty if list_contains_summary_only? define_method internal_field_name do download_complete_record! unless new_record? || options[:list_complete] || complete_record_downloaded? - self.attributes[field_name] || ((association_type == :has_many) ? [] : nil) + self.attributes[field_name] || self.class.value_if_nil(association_type) end end end + + def value_if_nil(association_type, boxed_value = nil) + case association_type + when :has_many + [boxed_value].compact + when :has_array + [boxed_value].compact + when :belongs_to + boxed_value + end + end end diff --git a/lib/xeroizer/record/xml_helper.rb b/lib/xeroizer/record/xml_helper.rb index d2d0f8c0..054c5a4b 100644 --- a/lib/xeroizer/record/xml_helper.rb +++ b/lib/xeroizer/record/xml_helper.rb @@ -124,6 +124,19 @@ def xml_value_from_field(b, field, value) nil end + when :has_array + # TODO: instead of doing field[:api_name].singularize + # store the field name on the has_array subject class somewhere + if value.size > 0 + b.tag!(field[:api_name]) do + value.each do |items| + items.value.each do |v| + b.tag!(field[:api_name].singularize, v.to_s) + end + end + end + end + end end diff --git a/test/stub_responses/payroll_employees.xml b/test/stub_responses/payroll_employees.xml new file mode 100644 index 00000000..77dbef09 --- /dev/null +++ b/test/stub_responses/payroll_employees.xml @@ -0,0 +1,15 @@ + + a1b8088e-4166-48e9-9e29-5b5919cac0a0 + OK + Xeroizer test + 2013-05-30T05:25:03.0599507Z + + + 07c60af4-5c86-44d9-8146-fab3733a087c + ACTIVE + Alex + Ghiculescu + 2013-04-18T01:29:53 + + + \ No newline at end of file diff --git a/test/stub_responses/records/payroll_employee-07c60af4-5c86-44d9-8146-fab3733a087c.xml b/test/stub_responses/records/payroll_employee-07c60af4-5c86-44d9-8146-fab3733a087c.xml new file mode 100644 index 00000000..31a929c1 --- /dev/null +++ b/test/stub_responses/records/payroll_employee-07c60af4-5c86-44d9-8146-fab3733a087c.xml @@ -0,0 +1,147 @@ + + 07c60af4-5c86-44d9-8146-fab3733a087d + OK + Demo AU + 2011-05-31T01:18:05.1105867Z + + + 07c60af4-5c86-44d9-8146-fab3733a087c + James + Lebron + ACTIVE + JL@madeup.email.com + 1978-08-13T00:00:00 + M + + 123 Main St + St. Kilda + VIC + 3182 + AUSTRALIA + + 0400-000-123 + 408-230-9732 + 2012-01-30T00:00:00 + 72e962d1-fcac-4083-8a71-742bb3e7ae14 + cb8e4706-2fdc-4170-aebd-0ffb855557f5 + 2013-04-01T23:02:36 + false + + false + true + true + false + false + false + 2013-04-26T00:04:48 + + + + Salary + James Lebron Savings + 122344 + 345678 + false + 20.0000 + + + Salary + James Lebron + 123443 + 2345678 + true + + + + 2012-07-01T00:00:00 + + + 72e962d1-fcac-4083-8a71-742bb3e7ae14 + 13333.33 + + + + + 59cd9d04-4521-4cc3-93ac-7841651ff407 + 40.00 + + + 4000.00 + + + 4333d5cd-53a5-4c31-98e5-a8b4e5676b0b + SGC + 3999.99 + + + + + + 742998cb-7584-4ecf-aa88-d694f59c50f9 + 100.0000 + + + 3bc7b584-a49f-40c6-ac5e-891f382785c9 + 40.0000 + + + + + + + 72e962d1-fcac-4083-8a71-742bb3e7ae14 + ANNUALSALARY + 40000.00 + 38.0000 + + + + + 59cd9d04-4521-4cc3-93ac-7841651ff407 + FIXEDAMOUNT + 10.00 + + + + + 4333d5cd-53a5-4c31-98e5-a8b4e5676b0b + SGC + PERCENTAGEOFEARNINGS + 450.00 + 478 + 826 + 9.0000 + + + 4333d5cd-53a5-4c31-98e5-a8b4e5676b0b + SALARYSACRIFICE + FIXEDAMOUNT + 478 + 826 + 50.0000 + + + + + + 742998cb-7584-4ecf-aa88-d694f59c50f9 + FIXEDAMOUNTEACHPERIOD + 152.0000 + + + 3bc7b584-a49f-40c6-ac5e-891f382785c9 + FIXEDAMOUNTEACHPERIOD + 76.0000 + + + + + + 4333d5cd-53a5-4c31-98e5-a8b4e5676b0b + 2187a42b-639a-45cb-9eed-cd4ae488306a + 1234 + + + + + \ No newline at end of file diff --git a/test/test_helper.rb b/test/test_helper.rb index e3a244e5..cd89bd77 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,5 +1,5 @@ require "rubygems" - +require 'debugger' require 'test/unit' require 'mocha' require 'shoulda' @@ -46,14 +46,26 @@ def get_report_xml(report_type) end def mock_api(model_name) - @client.stubs(:http_get).with {|client, url, params| url =~ /#{model_name}$/ }.returns(get_record_xml("#{model_name.underscore.pluralize}".to_sym)) - @client.send("#{model_name.singularize}".to_sym).all.each do | record | - @client.stubs(:http_get).with {|client, url, params| url =~ /#{model_name}\/#{record.id}$/ }.returns(get_record_xml("#{model_name.underscore.singularize}".to_sym, record.id)) + client_for_stubbing.stubs(:http_get).with { |client, url, params| url =~ /#{model_name}$/ }.returns(get_record_xml("#{model_name_for_file(model_name).underscore.pluralize}".to_sym)) + client_for_stubbing.send("#{model_name.singularize}".to_sym).all.each do | record | + client_for_stubbing.stubs(:http_get).with {|client, url, params| url =~ /#{model_name}\/#{record.id}$/ }.returns(get_record_xml("#{model_name_for_file(model_name).underscore.singularize}".to_sym, record.id)) end end def mock_report_api(report_type) - @client.stubs(:http_get).with { | client, url, params | url =~ /Reports\/#{report_type}$/ }.returns(get_report_xml(report_type)) + client_for_stubbing.stubs(:http_get).with { | client, url, params | url =~ /Reports\/#{report_type}$/ }.returns(get_report_xml(report_type)) + end + + def client_for_stubbing + payroll_application? ? @client.application : @client + end + + def model_name_for_file(model_name) + payroll_application? ? "payroll_#{model_name}" : model_name + end + + def payroll_application? + @client.is_a? Xeroizer::PayrollApplication end end diff --git a/test/unit/models/payroll/timesheet_test.rb b/test/unit/models/payroll/timesheet_test.rb new file mode 100644 index 00000000..b9da6f9b --- /dev/null +++ b/test/unit/models/payroll/timesheet_test.rb @@ -0,0 +1,33 @@ +require 'test_helper' + +class TimesheetTest < Test::Unit::TestCase + include TestHelper + + def setup + @client = Xeroizer::PublicApplication.new(CONSUMER_KEY, CONSUMER_SECRET).payroll + mock_api('Employees') + @employee = @client.Employee.first + end + + test "create invalid timesheet" do + timesheet = @client.Timesheet.build + assert_equal(false, timesheet.valid?) + end + + test "create valid timesheet" do + timesheet = @client.Timesheet.build(employee_id: @employee.id, start_date: Date.today - 1.week, end_date: Date.today) + assert_equal(true, timesheet.valid?) + end + + test "create timesheet with units" do + timesheet = @client.Timesheet.build(status: 'DRAFT', employee_id: @employee.id, start_date: Date.today - 1.week, end_date: Date.today) + + timesheet_line = timesheet.add_timesheet_line(earnings_rate_id: @employee.ordinary_earnings_rate_id) + timesheet_line.add_to_number_of_units([8.00, 8.00, 8.00, 8.00, 8.00, 0.00, 0.00]) + + doc = Nokogiri::XML timesheet.to_xml + assert_equal 1, doc.xpath("/Timesheet/TimesheetLines/TimesheetLine").size + assert_equal 7, doc.xpath("/Timesheet/TimesheetLines/TimesheetLine/NumberOfUnits/NumberOfUnit").size + end + +end From 27af99ff82bf31a4a45d5dd6ef978ef10ee0b283 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Fri, 31 May 2013 14:27:10 +1000 Subject: [PATCH 02/82] add pay items (working and tested) and leave applications (work in progress) --- lib/xeroizer.rb | 48 ++--- lib/xeroizer/models/payroll/deduction_type.rb | 26 +++ lib/xeroizer/models/payroll/earnings_rate.rb | 32 +++ .../models/payroll/leave_application.rb | 34 ++++ lib/xeroizer/models/payroll/leave_period.rb | 20 ++ lib/xeroizer/models/payroll/leave_type.rb | 28 +++ lib/xeroizer/models/payroll/pay_items.rb | 27 +++ .../models/payroll/reimbursement_type.rb | 24 +++ lib/xeroizer/models/payroll/timesheet_line.rb | 2 +- lib/xeroizer/payroll_application.rb | 1 + lib/xeroizer/record/base_model.rb | 30 ++- .../record/model_definition_helper.rb | 6 +- .../record/record_association_helper.rb | 5 +- lib/xeroizer/record/xml_helper.rb | 29 +-- .../payroll_leave_applications.xml | 15 ++ test/stub_responses/payroll_pay_items.xml | 187 ++++++++++++++++++ ...n-07c60af4-5c86-44d9-8146-fab3733a087c.xml | 25 +++ test/test_helper.rb | 3 +- .../models/payroll/leave_application_test.rb | 22 +++ test/unit/models/payroll/pay_items_test.rb | 24 +++ 20 files changed, 533 insertions(+), 55 deletions(-) create mode 100644 lib/xeroizer/models/payroll/deduction_type.rb create mode 100644 lib/xeroizer/models/payroll/earnings_rate.rb create mode 100644 lib/xeroizer/models/payroll/leave_application.rb create mode 100644 lib/xeroizer/models/payroll/leave_period.rb create mode 100644 lib/xeroizer/models/payroll/leave_type.rb create mode 100644 lib/xeroizer/models/payroll/pay_items.rb create mode 100644 lib/xeroizer/models/payroll/reimbursement_type.rb create mode 100644 test/stub_responses/payroll_leave_applications.xml create mode 100644 test/stub_responses/payroll_pay_items.xml create mode 100644 test/stub_responses/records/payroll_leave_application-07c60af4-5c86-44d9-8146-fab3733a087c.xml create mode 100644 test/unit/models/payroll/leave_application_test.rb create mode 100644 test/unit/models/payroll/pay_items_test.rb diff --git a/lib/xeroizer.rb b/lib/xeroizer.rb index 1d314e8d..edd322a0 100644 --- a/lib/xeroizer.rb +++ b/lib/xeroizer.rb @@ -30,40 +30,20 @@ require 'xeroizer/configuration' # Include models -require 'xeroizer/models/account' -require 'xeroizer/models/address' -require 'xeroizer/models/branding_theme' -require 'xeroizer/models/bank_transaction' -require 'xeroizer/models/bank_account' -require 'xeroizer/models/contact' -require 'xeroizer/models/contact_group' -require 'xeroizer/models/credit_note' -require 'xeroizer/models/currency' -require 'xeroizer/models/employee' -require 'xeroizer/models/invoice' -require 'xeroizer/models/item' -require 'xeroizer/models/item_purchase_details' -require 'xeroizer/models/item_sales_details' -require 'xeroizer/models/journal' -require 'xeroizer/models/journal_line' -require 'xeroizer/models/line_item' -require 'xeroizer/models/manual_journal' -require 'xeroizer/models/manual_journal_line' -require 'xeroizer/models/option' -require 'xeroizer/models/organisation' -require 'xeroizer/models/payment' -require 'xeroizer/models/phone' -require 'xeroizer/models/tax_rate' -require 'xeroizer/models/tracking_category' -require 'xeroizer/models/tracking_category_child' -require 'xeroizer/models/journal_line_tracking_category' - -require 'xeroizer/models/payroll/home_address' -require 'xeroizer/models/payroll/bank_account' -require 'xeroizer/models/payroll/employee' -require 'xeroizer/models/payroll/timesheet' -require 'xeroizer/models/payroll/timesheet_line' -require 'xeroizer/models/payroll/number_of_unit' +['account','address','branding_theme','bank_transaction','bank_account','contact','contact_group', + 'credit_note','currency','employee','invoice','item','item_purchase_details','item_sales_details', + 'journal','journal_line','line_item','manual_journal','manual_journal_line','option','organisation', + 'payment','phone','tax_rate','tracking_category','tracking_category_child', + 'journal_line_tracking_category'].each do |model| + require "xeroizer/models/#{model}" +end + +# Include payroll models +['home_address', 'bank_account', 'employee', 'timesheet', 'timesheet_line', 'number_of_unit', + 'leave_application', 'leave_period', 'pay_items', 'deduction_type', 'earnings_rate', + 'reimbursement_type', 'leave_type'].each do |payroll_model| + require "xeroizer/models/payroll/#{payroll_model}" +end require 'xeroizer/report/factory' diff --git a/lib/xeroizer/models/payroll/deduction_type.rb b/lib/xeroizer/models/payroll/deduction_type.rb new file mode 100644 index 00000000..34e858ce --- /dev/null +++ b/lib/xeroizer/models/payroll/deduction_type.rb @@ -0,0 +1,26 @@ +module Xeroizer + module Record + module Payroll + + class DeductionTypeModel < PayrollBaseModel + + end + + class DeductionType < PayrollBase + + string :name + string :account_code # http://developer.xero.com/api/Accounts + boolean :reduces_super + boolean :reduces_tax + + guid :deduction_type_id + + datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC' + + validates_presence_of :name, :account_code, :reduces_tax, :reduces_super + + end + + end + end +end diff --git a/lib/xeroizer/models/payroll/earnings_rate.rb b/lib/xeroizer/models/payroll/earnings_rate.rb new file mode 100644 index 00000000..43865afb --- /dev/null +++ b/lib/xeroizer/models/payroll/earnings_rate.rb @@ -0,0 +1,32 @@ +module Xeroizer + module Record + module Payroll + + class EarningsRateModel < PayrollBaseModel + + end + + class EarningsRate < PayrollBase + + string :name + string :account_code # http://developer.xero.com/api/Accounts + string :type_of_units + boolean :is_exempt_from_tax + boolean :is_exempt_from_super + string :earnings_type # http://developer.xero.com/payroll-api/types-and-codes/#EarningsTypes + + guid :earnings_rate_id + string :rate_type # http://developer.xero.com/payroll-api/types-and-codes/#EarningsRateTypes + decimal :multiplier + boolean :accrue_leave + decimal :amount + + datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC' + + validates_presence_of :name, :account_code, :type_of_units, :is_exempt_from_super, :is_exempt_from_tax, :earnings_type + + end + + end + end +end diff --git a/lib/xeroizer/models/payroll/leave_application.rb b/lib/xeroizer/models/payroll/leave_application.rb new file mode 100644 index 00000000..060b4500 --- /dev/null +++ b/lib/xeroizer/models/payroll/leave_application.rb @@ -0,0 +1,34 @@ +module Xeroizer + module Record + module Payroll + + class LeaveApplicationModel < PayrollBaseModel + + set_permissions :read, :write, :update + + # TODO: calling #get on this requries an employee ID + end + + class LeaveApplication < PayrollBase + + set_primary_key :leave_application_id + + guid :leave_application_id + guid :employee_id + guid :leave_type_id + + string :title + date :start_date + date :end_date + + string :description + + has_many :leave_periods + + validates_presence_of :employee_id, :leave_type_id, :title, :start_date, :end_date + + end + + end + end +end diff --git a/lib/xeroizer/models/payroll/leave_period.rb b/lib/xeroizer/models/payroll/leave_period.rb new file mode 100644 index 00000000..beb40b26 --- /dev/null +++ b/lib/xeroizer/models/payroll/leave_period.rb @@ -0,0 +1,20 @@ +module Xeroizer + module Record + module Payroll + + class LeavePeriodModel < PayrollBaseModel + + end + + class LeavePeriod < PayrollBase + + decimal :number_of_units + date :pay_period_start_date + date :pay_period_end_date + string :leave_period_status + + end + + end + end +end diff --git a/lib/xeroizer/models/payroll/leave_type.rb b/lib/xeroizer/models/payroll/leave_type.rb new file mode 100644 index 00000000..84f065be --- /dev/null +++ b/lib/xeroizer/models/payroll/leave_type.rb @@ -0,0 +1,28 @@ +module Xeroizer + module Record + module Payroll + + class LeaveTypeModel < PayrollBaseModel + + end + + class LeaveType < PayrollBase + + string :name + string :type_of_units + boolean :is_paid_leave + boolean :show_on_payslip + + guid :leave_type_id + decimal :normal_entitlement + decimal :leave_loading_rate + + datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC' + + validates_presence_of :name, :type_of_units, :is_paid_leave, :show_on_payslip + + end + + end + end +end diff --git a/lib/xeroizer/models/payroll/pay_items.rb b/lib/xeroizer/models/payroll/pay_items.rb new file mode 100644 index 00000000..12ed7345 --- /dev/null +++ b/lib/xeroizer/models/payroll/pay_items.rb @@ -0,0 +1,27 @@ +module Xeroizer + module Record + module Payroll + + class PayItemModel < PayrollBaseModel + + set_permissions :read, :write, :update + + set_all_children_are_subtypes true + set_xml_root_name 'PayItems' + set_xml_node_name 'PayItems' + end + + class PayItem < PayrollBase + + set_primary_key false + + has_many :earnings_rates + has_many :deduction_types + has_many :leave_types + has_many :reimbursement_types + + end + + end + end +end diff --git a/lib/xeroizer/models/payroll/reimbursement_type.rb b/lib/xeroizer/models/payroll/reimbursement_type.rb new file mode 100644 index 00000000..2584a1d7 --- /dev/null +++ b/lib/xeroizer/models/payroll/reimbursement_type.rb @@ -0,0 +1,24 @@ +module Xeroizer + module Record + module Payroll + + class ReimbursementTypeModel < PayrollBaseModel + + end + + class ReimbursementType < PayrollBase + + string :name + string :account_code # http://developer.xero.com/api/Accounts + + guid :reimbursement_type_id + + datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC' + + validates_presence_of :name, :account_code + + end + + end + end +end diff --git a/lib/xeroizer/models/payroll/timesheet_line.rb b/lib/xeroizer/models/payroll/timesheet_line.rb index 2c46d612..5a9f664e 100644 --- a/lib/xeroizer/models/payroll/timesheet_line.rb +++ b/lib/xeroizer/models/payroll/timesheet_line.rb @@ -11,7 +11,7 @@ class TimesheetLine < PayrollBase guid :earnings_rate_id guid :tracking_item_id # TODO: belongs to a http://developer.xero.com/api/Tracking%20Categories/ (optionally? the docs don't have any examples) - has_array :number_of_units + has_array :number_of_units, :api_child_name => 'NumberOfUnit' datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC' diff --git a/lib/xeroizer/payroll_application.rb b/lib/xeroizer/payroll_application.rb index 95bd7a18..b9c509cc 100644 --- a/lib/xeroizer/payroll_application.rb +++ b/lib/xeroizer/payroll_application.rb @@ -20,6 +20,7 @@ def self.record(record_type) record :Employee record :Timesheet + record :PayItem def initialize(application) @application = application diff --git a/lib/xeroizer/record/base_model.rb b/lib/xeroizer/record/base_model.rb index 9c300bf2..bcade227 100644 --- a/lib/xeroizer/record/base_model.rb +++ b/lib/xeroizer/record/base_model.rb @@ -18,6 +18,7 @@ class InvalidPermissionError < StandardError class_inheritable_attributes :xml_root_name class_inheritable_attributes :optional_xml_root_name class_inheritable_attributes :xml_node_name + class_inheritable_attributes :all_children_are_subtypes include BaseModelHttpProxy @@ -65,6 +66,14 @@ def set_xml_root_name(root_name) def set_optional_xml_root_name(optional_root_name) self.optional_root_name = optional_root_name end + + # Usually the xml structure will be + # If this is true, the tag isn't expected. So it would be + # + # Example: http://developer.xero.com/payroll-api/PayItems/#GET + def set_all_children_are_subtypes(boolean) + self.all_children_are_subtypes = boolean + end end @@ -117,7 +126,7 @@ def all(options = {}) raise MethodNotAllowed.new(self, :all) unless self.class.permissions[:read] response_xml = http_get(parse_params(options)) response = parse_response(response_xml, options) - response.response_items || [] + response_items = response.response_items || [] end # Helper method to retrieve just the first element from @@ -171,6 +180,9 @@ def parse_response(response_xml, options = {}) if model_name == response_model_name @response = response parse_records(response, elements, (options[:base_module] || Xeroizer::Record)) + elsif self.class.all_children_are_subtypes && self.class.xml_root_name == elements.first.parent.name + @response = response + parse_records(response, elements, (options[:base_module] || Xeroizer::Record), true) end end end @@ -178,17 +190,27 @@ def parse_response(response_xml, options = {}) protected # Parse the records part of the XML response and builds model instances as necessary. - def parse_records(response, elements, base_module) + def parse_records(response, elements, base_module, all_children_are_subtypes = false) elements.each do | element | - new_record = model_class.build_from_node(element, self, base_module) + new_record = model_class.build_from_node(element, self, base_module, all_children_are_subtypes) if element.attribute('status').try(:value) == 'ERROR' new_record.errors = [] element.xpath('.//ValidationError').each do |err| new_record.errors << err.text.gsub(/^\s+/, '').gsub(/\s+$/, '') end end - response.response_items << new_record + if all_children_are_subtypes + if response.response_items.count == 0 + response.response_items << new_record + else + field_to_fill = model_class.fields.find {|f| new_record[f[0]].count > 0} + (response.response_items.first[field_to_fill[0]] = new_record[field_to_fill[0]]) unless field_to_fill.nil? + end + else + response.response_items << new_record + end end + response.response_items end def to_bulk_xml(records, builder = Builder::XmlMarkup.new(:indent => 2)) diff --git a/lib/xeroizer/record/model_definition_helper.rb b/lib/xeroizer/record/model_definition_helper.rb index a3121c4e..10487e08 100644 --- a/lib/xeroizer/record/model_definition_helper.rb +++ b/lib/xeroizer/record/model_definition_helper.rb @@ -16,8 +16,9 @@ def set_possible_primary_keys(*args) end # Set the actual Xero primary key for this record. + # Set this to `false` if this type has no primary key def set_primary_key(primary_key_name) - self.primary_key_name = primary_key_name + self.primary_key_name = primary_key_name unless primary_key_name == false end # Whether this record type's list results contain summary data only. @@ -65,6 +66,7 @@ def define_simple_attribute(field_name, field_type, options, value_if_nil = nil) self.fields[field_name] = options.merge({ :internal_name => internal_field_name, :api_name => options[:api_name] || field_name.to_s.camelize, + :api_child_name => options[:api_child_name] || (options[:api_name] || field_name.to_s.camelize).singularize, :type => field_type }) define_method internal_field_name do @@ -85,7 +87,7 @@ module InstanceMethods # Returns the value of the Xero primary key for this record if it exists. def id - self[self.class.primary_key_name] + self[self.class.primary_key_name] unless self.class.primary_key_name.nil? end # Sets the value of the Xero primary key for this record if it exists. diff --git a/lib/xeroizer/record/record_association_helper.rb b/lib/xeroizer/record/record_association_helper.rb index 5830eaee..8f822155 100644 --- a/lib/xeroizer/record/record_association_helper.rb +++ b/lib/xeroizer/record/record_association_helper.rb @@ -50,8 +50,8 @@ def has_array(field_name, options = {}) # add_to_records([value, value, value]) # add_to_records(value) values = [] - if args.size == 1 && !args.first.is_a?(Array) - values = [args.first] + if args.size == 1 && args.first.is_a?(Array) + values = args.first elsif args.size > 0 values = args else @@ -65,6 +65,7 @@ def has_array(field_name, options = {}) last_record = nil values.each do |value| record = record_class.build(value, model_parent) + raise StandardError.new("Record #{record.class.name} is not a #{record_class.name}.") unless record.is_a?(record_class) self.attributes[field_name] ||= [] self.attributes[field_name] << record last_record = record diff --git a/lib/xeroizer/record/xml_helper.rb b/lib/xeroizer/record/xml_helper.rb index 054c5a4b..de373328 100644 --- a/lib/xeroizer/record/xml_helper.rb +++ b/lib/xeroizer/record/xml_helper.rb @@ -12,10 +12,11 @@ def self.included(base) module ClassMethods # Build a record instance from the XML node. - def build_from_node(node, parent, base_module) + def build_from_node(node, parent, base_module, all_children_are_subtypes = false) record = new(parent) node.elements.each do | element | - field = self.fields[element.name.to_s.underscore.to_sym] + element_name = all_children_are_subtypes ? element.name.to_s.pluralize : element.name.to_s + field = self.fields[element_name.underscore.to_sym] if field value = case field[:type] when :guid then element.text @@ -32,16 +33,25 @@ def build_from_node(node, parent, base_module) when :has_many if element.element_children.size > 0 - sub_field_name = field[:model_name] ? field[:model_name].to_sym : element.children.first.name.to_sym + sub_field_name = field[:model_name] ? field[:model_name].to_sym : (all_children_are_subtypes ? element.name : element.children.first.name).to_sym sub_parent = record.new_model_class(sub_field_name) - element.children.inject([]) do | list, element | - list << base_module.const_get(sub_field_name).build_from_node(element, sub_parent, base_module) + if all_children_are_subtypes + base_module.const_get(sub_field_name).build_from_node(element, sub_parent, base_module) + else + element.children.inject([]) do | list, element | + list << base_module.const_get(sub_field_name).build_from_node(element, sub_parent, base_module) + end end end + when :has_array + raise 'TODO' + end if field[:calculated] record.attributes[field[:internal_name]] = value + elsif all_children_are_subtypes + record.send("add_#{field[:internal_name].to_s.singularize}".to_sym, value) else record.send("#{field[:internal_name]}=".to_sym, value) end @@ -125,16 +135,13 @@ def xml_value_from_field(b, field, value) end when :has_array - # TODO: instead of doing field[:api_name].singularize - # store the field name on the has_array subject class somewhere if value.size > 0 b.tag!(field[:api_name]) do - value.each do |items| - items.value.each do |v| - b.tag!(field[:api_name].singularize, v.to_s) - end + value.each do |v| + b.tag!(field[:api_child_name], v.value) end end + nil end end diff --git a/test/stub_responses/payroll_leave_applications.xml b/test/stub_responses/payroll_leave_applications.xml new file mode 100644 index 00000000..77dbef09 --- /dev/null +++ b/test/stub_responses/payroll_leave_applications.xml @@ -0,0 +1,15 @@ + + a1b8088e-4166-48e9-9e29-5b5919cac0a0 + OK + Xeroizer test + 2013-05-30T05:25:03.0599507Z + + + 07c60af4-5c86-44d9-8146-fab3733a087c + ACTIVE + Alex + Ghiculescu + 2013-04-18T01:29:53 + + + \ No newline at end of file diff --git a/test/stub_responses/payroll_pay_items.xml b/test/stub_responses/payroll_pay_items.xml new file mode 100644 index 00000000..cd80c0fb --- /dev/null +++ b/test/stub_responses/payroll_pay_items.xml @@ -0,0 +1,187 @@ + + a1b8088e-4166-48e9-9e29-5b5919cac0a0 + OK + Xeroizer test + 2013-05-30T05:25:03.0599507Z + + + + eca71b79-edab-4c3f-967f-a405453bac08 + Ordinary Hours + ORDINARYTIMEEARNINGS + RATEPERUNIT + 477 + Hours + false + false + 2013-04-09T23:45:25 + + + f8eb8381-7db0-4de3-ada0-e03bbd1d811f + Overtime Hours (exempt from super) + OVERTIMEEARNINGS + MULTIPLE + 477 + 1.5000 + false + true + false + 2013-04-09T23:45:25 + + + 694d0065-f594-48f7-89c0-5cfd7a0c4c92 + Allowances subject to tax withholding and super + ALLOWANCE + RATEPERUNIT + 477 + Fixed Amount + false + false + 2013-04-09T23:45:25 + + + 92d6ae67-4302-4561-9000-90163cac8eb8 + Allowances exempt from tax withholding and super + ALLOWANCE + RATEPERUNIT + 477 + Fixed Amount + true + true + 2013-04-09T23:45:25 + + + + + f9be43ee-1876-4e67-a295-2a30eadd1942 + Union Fees/Subscriptions + 850 + false + false + 2013-04-09T23:45:25 + + + f70fcea1-157c-4a1a-8724-369160e5cb5c + Lease Payments + 850 + true + true + 2013-04-09T23:45:25 + + + 8d6e580f-37e1-48cf-80fe-e591008a251f + FBT + 850 + true + true + 2013-04-09T23:45:25 + + + eabff3b0-2fdc-4e3b-955c-30e113829c74 + Other Pre-Tax Deductions + 850 + true + true + 2013-04-09T23:45:25 + + + c0b2f074-883e-451b-8863-185fd2403cef + Other Post-Tax Deductions + 850 + false + false + 2013-04-09T23:45:25 + + + + + d4263307-f387-4c69-9525-7482d742ef50 + Travel National Costs + 493 + 2013-04-09T23:45:25 + + + 7ecdc711-ea65-469a-ab9d-2a63f0753129 + Other Reimbursable Costs + 850 + 2013-04-09T23:45:25 + + + + + bd2ead8d-f8f9-480a-8a71-260222ed1a34 + Annual Leave + Hours + 152.0000 + true + true + 2013-04-09T23:45:25 + + + cd61fd41-671b-4d1f-ad6e-fbcb1eca3fc3 + Long Service Leave + Hours + true + false + 2013-04-09T23:45:25 + + + c06359d4-ee61-4f74-9c74-f94ef6a850e3 + Parental Leave (unpaid) + Hours + false + false + 2013-04-09T23:45:25 + + + 271814d6-1087-45f2-923a-8301113a298b + Personal/Carer's Leave + Hours + 76.0000 + true + false + 2013-04-09T23:45:25 + + + f3a5f2f8-c14e-4223-ab58-4e779e0f6f2c + Carer's Leave (unpaid) + Hours + false + false + 2013-04-09T23:45:25 + + + 30ca48d0-fe04-40c2-b590-f5191159259f + Compassionate Leave (paid) + Hours + true + false + 2013-04-09T23:45:25 + + + 1e1765bc-1dbc-4ac9-b6cd-9f9f3290d8a4 + Compassionate Leave (unpaid) + Hours + false + false + 2013-04-09T23:45:25 + + + 42d46756-9f2b-4456-ad44-8c889ff4a5bd + Community Service Leave + Hours + false + false + 2013-04-09T23:45:25 + + + 7ea96a9f-d7c2-4de1-a3ac-bf89980f1890 + Other Unpaid Leave + Hours + false + false + 2013-04-09T23:45:25 + + + + \ No newline at end of file diff --git a/test/stub_responses/records/payroll_leave_application-07c60af4-5c86-44d9-8146-fab3733a087c.xml b/test/stub_responses/records/payroll_leave_application-07c60af4-5c86-44d9-8146-fab3733a087c.xml new file mode 100644 index 00000000..e2ca22e2 --- /dev/null +++ b/test/stub_responses/records/payroll_leave_application-07c60af4-5c86-44d9-8146-fab3733a087c.xml @@ -0,0 +1,25 @@ + + a1b8088e-4166-48e9-9e29-5b5919cac0a0 + OK + Xeroizer test + 2013-05-30T05:25:03.0599507Z + + + e0eb6747-7c17-4075-b804-989f8d4e5d39 + 07c60af4-5c86-44d9-8146-fab3733a087c + 742998cb-7584-4ecf-aa88-d694f59c50f9 + + + 2013-04-29T00:00:00 + 2013-05-12T00:00:00 + SCHEDULED + 22.8000 + + + Annual Leave + 2013-05-08T00:00:00 + 2013-05-10T00:00:00 + 2013-04-01T23:02:36 + + + \ No newline at end of file diff --git a/test/test_helper.rb b/test/test_helper.rb index cd89bd77..4d3d63bf 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -47,7 +47,8 @@ def get_report_xml(report_type) def mock_api(model_name) client_for_stubbing.stubs(:http_get).with { |client, url, params| url =~ /#{model_name}$/ }.returns(get_record_xml("#{model_name_for_file(model_name).underscore.pluralize}".to_sym)) - client_for_stubbing.send("#{model_name.singularize}".to_sym).all.each do | record | + @client.send("#{model_name.singularize}".to_sym).all.each do | record | + next if record.id.nil? client_for_stubbing.stubs(:http_get).with {|client, url, params| url =~ /#{model_name}\/#{record.id}$/ }.returns(get_record_xml("#{model_name_for_file(model_name).underscore.singularize}".to_sym, record.id)) end end diff --git a/test/unit/models/payroll/leave_application_test.rb b/test/unit/models/payroll/leave_application_test.rb new file mode 100644 index 00000000..b83d5a58 --- /dev/null +++ b/test/unit/models/payroll/leave_application_test.rb @@ -0,0 +1,22 @@ +require 'test_helper' + +class LeaveApplicationTest < Test::Unit::TestCase + include TestHelper + + def setup + @client = Xeroizer::PublicApplication.new(CONSUMER_KEY, CONSUMER_SECRET).payroll + #mock_api('Employees') + #mock_api('LeaveApplications') + end + +=begin + test "can get leave applications using employee ID" do + application = @client.LeaveApplication.find(@client.Employee.first.id) + assert_not_nil application + assert_equal 1, application.leave_periods.size + application.add_leave_periods(number_of_units: 48.00, pay_period_end_date: Date.today) + application.save + assert_equal 2, application.leave_periods.size + end +=end +end diff --git a/test/unit/models/payroll/pay_items_test.rb b/test/unit/models/payroll/pay_items_test.rb new file mode 100644 index 00000000..781dfa3e --- /dev/null +++ b/test/unit/models/payroll/pay_items_test.rb @@ -0,0 +1,24 @@ +require 'test_helper' + +class PayItemsTest < Test::Unit::TestCase + include TestHelper + + def setup + @client = Xeroizer::PublicApplication.new(CONSUMER_KEY, CONSUMER_SECRET).payroll + mock_api('PayItems') + end + + test "get all" do + pay_items = @client.PayItem.first + assert_equal 4, pay_items.earnings_rates.size + assert_equal 5, pay_items.deduction_types.size + assert_equal 2, pay_items.reimbursement_types.size + assert_equal 9, pay_items.leave_types.size + + doc = Nokogiri::XML pay_items.to_xml + assert_equal 4, doc.xpath("/PayItems/EarningsRates/EarningsRate").size + assert_equal 5, doc.xpath("/PayItems/DeductionTypes/DeductionType").size + assert_equal 2, doc.xpath("/PayItems/ReimbursementTypes/ReimbursementType").size + assert_equal 9, doc.xpath("/PayItems/LeaveTypes/LeaveType").size + end +end From 4db3df8772a62a2af0c3ec164ddd604e58cb0643 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Fri, 31 May 2013 15:04:23 +1000 Subject: [PATCH 03/82] add payroll calendars --- lib/xeroizer.rb | 2 +- .../models/payroll/payroll_calendar.rb | 28 ++++++++++++++++++ lib/xeroizer/payroll_application.rb | 2 ++ .../payroll_payroll_calendars.xml | 29 +++++++++++++++++++ ...r-49713875-ad73-492c-b6ac-2d265a5fe862.xml | 15 ++++++++++ ...r-a17394fe-fa23-4d4a-8e2f-a19217bc6b4f.xml | 15 ++++++++++ ...r-bfac31bd-ea62-4fc8-a5e7-7965d9504b15.xml | 15 ++++++++++ .../models/payroll/payroll_calendar_test.rb | 23 +++++++++++++++ 8 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 lib/xeroizer/models/payroll/payroll_calendar.rb create mode 100644 test/stub_responses/payroll_payroll_calendars.xml create mode 100644 test/stub_responses/records/payroll_payroll_calendar-49713875-ad73-492c-b6ac-2d265a5fe862.xml create mode 100644 test/stub_responses/records/payroll_payroll_calendar-a17394fe-fa23-4d4a-8e2f-a19217bc6b4f.xml create mode 100644 test/stub_responses/records/payroll_payroll_calendar-bfac31bd-ea62-4fc8-a5e7-7965d9504b15.xml create mode 100644 test/unit/models/payroll/payroll_calendar_test.rb diff --git a/lib/xeroizer.rb b/lib/xeroizer.rb index edd322a0..9b9c5333 100644 --- a/lib/xeroizer.rb +++ b/lib/xeroizer.rb @@ -41,7 +41,7 @@ # Include payroll models ['home_address', 'bank_account', 'employee', 'timesheet', 'timesheet_line', 'number_of_unit', 'leave_application', 'leave_period', 'pay_items', 'deduction_type', 'earnings_rate', - 'reimbursement_type', 'leave_type'].each do |payroll_model| + 'reimbursement_type', 'leave_type', 'payroll_calendar'].each do |payroll_model| require "xeroizer/models/payroll/#{payroll_model}" end diff --git a/lib/xeroizer/models/payroll/payroll_calendar.rb b/lib/xeroizer/models/payroll/payroll_calendar.rb new file mode 100644 index 00000000..930e9f5a --- /dev/null +++ b/lib/xeroizer/models/payroll/payroll_calendar.rb @@ -0,0 +1,28 @@ +module Xeroizer + module Record + module Payroll + + class PayrollCalendarModel < PayrollBaseModel + + set_permissions :read, :write + + end + + class PayrollCalendar < PayrollBase + + set_primary_key :payroll_calendar_id + + guid :payroll_calendar_id + + string :name + string :calendar_type # http://developer.xero.com/payroll-api/types-and-codes/#CalendarTypes + date :start_date + date :payment_date + + validates_presence_of :name, :calendar_type, :start_date, :payment_date + + end + + end + end +end diff --git a/lib/xeroizer/payroll_application.rb b/lib/xeroizer/payroll_application.rb index b9c509cc..5e367b3d 100644 --- a/lib/xeroizer/payroll_application.rb +++ b/lib/xeroizer/payroll_application.rb @@ -21,6 +21,8 @@ def self.record(record_type) record :Employee record :Timesheet record :PayItem + record :PayrollCalendar + record :LeaveApplication def initialize(application) @application = application diff --git a/test/stub_responses/payroll_payroll_calendars.xml b/test/stub_responses/payroll_payroll_calendars.xml new file mode 100644 index 00000000..f3d4b418 --- /dev/null +++ b/test/stub_responses/payroll_payroll_calendars.xml @@ -0,0 +1,29 @@ + + a1b8088e-4166-48e9-9e29-5b5919cac0a0 + OK + Xeroizer test + 2013-05-30T05:25:03.0599507Z + + + FORTNIGHTLY + Fortnightly Calendar + 2012-08-17T00:00:00Z + a17394fe-fa23-4d4a-8e2f-a19217bc6b4f + 2012-08-01T00:00:00 + + + WEEKLY + Weekly Calendar + 2012-05-20T00:00:00Z + bfac31bd-ea62-4fc8-a5e7-7965d9504b15 + 2012-05-13T00:00:00 + + + WEEKLY + What + 2012-11-16T00:00:00Z + 49713875-ad73-492c-b6ac-2d265a5fe862 + 2012-11-08T00:00:00 + + + \ No newline at end of file diff --git a/test/stub_responses/records/payroll_payroll_calendar-49713875-ad73-492c-b6ac-2d265a5fe862.xml b/test/stub_responses/records/payroll_payroll_calendar-49713875-ad73-492c-b6ac-2d265a5fe862.xml new file mode 100644 index 00000000..4376f7c0 --- /dev/null +++ b/test/stub_responses/records/payroll_payroll_calendar-49713875-ad73-492c-b6ac-2d265a5fe862.xml @@ -0,0 +1,15 @@ + + 49713875-ad73-492c-b6ac-2d265a5fe862 + OK + Demo AU + 2011-05-31T01:18:05.1105867Z + + + WEEKLY + What + 2012-11-16T00:00:00Z + 49713875-ad73-492c-b6ac-2d265a5fe862 + 2012-11-08T00:00:00 + + + \ No newline at end of file diff --git a/test/stub_responses/records/payroll_payroll_calendar-a17394fe-fa23-4d4a-8e2f-a19217bc6b4f.xml b/test/stub_responses/records/payroll_payroll_calendar-a17394fe-fa23-4d4a-8e2f-a19217bc6b4f.xml new file mode 100644 index 00000000..5572b624 --- /dev/null +++ b/test/stub_responses/records/payroll_payroll_calendar-a17394fe-fa23-4d4a-8e2f-a19217bc6b4f.xml @@ -0,0 +1,15 @@ + + a17394fe-fa23-4d4a-8e2f-a19217bc6b4f + OK + Demo AU + 2011-05-31T01:18:05.1105867Z + + + FORTNIGHTLY + Fortnightly Calendar + 2012-08-17T00:00:00Z + a17394fe-fa23-4d4a-8e2f-a19217bc6b4f + 2012-08-01T00:00:00 + + + \ No newline at end of file diff --git a/test/stub_responses/records/payroll_payroll_calendar-bfac31bd-ea62-4fc8-a5e7-7965d9504b15.xml b/test/stub_responses/records/payroll_payroll_calendar-bfac31bd-ea62-4fc8-a5e7-7965d9504b15.xml new file mode 100644 index 00000000..c82e695e --- /dev/null +++ b/test/stub_responses/records/payroll_payroll_calendar-bfac31bd-ea62-4fc8-a5e7-7965d9504b15.xml @@ -0,0 +1,15 @@ + + bfac31bd-ea62-4fc8-a5e7-7965d9504b15 + OK + Demo AU + 2011-05-31T01:18:05.1105867Z + + + WEEKLY + Weekly Calendar + 2012-05-20T00:00:00Z + bfac31bd-ea62-4fc8-a5e7-7965d9504b15 + 2012-05-13T00:00:00 + + + \ No newline at end of file diff --git a/test/unit/models/payroll/payroll_calendar_test.rb b/test/unit/models/payroll/payroll_calendar_test.rb new file mode 100644 index 00000000..6f5df81e --- /dev/null +++ b/test/unit/models/payroll/payroll_calendar_test.rb @@ -0,0 +1,23 @@ +require 'test_helper' + +class PayrollCalendarTest < Test::Unit::TestCase + include TestHelper + + def setup + @client = Xeroizer::PublicApplication.new(CONSUMER_KEY, CONSUMER_SECRET).payroll + mock_api('PayrollCalendars') + end + + test "get all" do + calendars = @client.PayrollCalendar.all + assert_equal 3, calendars.size + end + + test "get one" do + calendar = @client.PayrollCalendar.first + assert_not_nil calendar + + doc = Nokogiri::XML calendar.to_xml + assert_equal 1, doc.xpath("/PayrollCalendar/PayrollCalendarID").size + end +end From e8a85035ead2284483050154231c6e1177a6ddab Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Sat, 1 Jun 2013 13:27:10 +1000 Subject: [PATCH 04/82] add payroll scopes. enable calling of #request_token (etc.) directly on a client.payroll. update readme with scope notes. --- README.md | 32 +++++++++++++++++++++++++++++ lib/xeroizer.rb | 1 + lib/xeroizer/payroll_application.rb | 3 +++ lib/xeroizer/scopes.rb | 7 +++++++ 4 files changed, 43 insertions(+) create mode 100644 lib/xeroizer/scopes.rb diff --git a/README.md b/README.md index 913e38b7..dffbcc67 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,22 @@ There are three methods of authentication detailed below: All methods of authentication require your OAuth consumer key and secret. This can be found for your application in the API management console at [http://api.xero.com](http://api.xero.com). +##### Payroll Applications + +Applications accessing the [Payroll API](http://developer.xero.com/payroll-api/) need to do things slightly differently. To change a client into a payroll client, call `.payroll` on the client. For example, for a public application, change: + +```ruby +client = Xeroizer::PublicApplication.new(YOUR_OAUTH_CONSUMER_KEY, YOUR_OAUTH_CONSUMER_SECRET) +``` + +to: + +```ruby +client = Xeroizer::PublicApplication.new(YOUR_OAUTH_CONSUMER_KEY, YOUR_OAUTH_CONSUMER_SECRET).payroll +``` + +Some kinds of applications will also need to provide scopes when navigating to the request URL. See below for more details. + ### Public Applications Public applications use a 3-legged authorisation process. A user will need to authorise your @@ -83,6 +99,22 @@ client.authorize_from_request(request_token.token, request_token.secret, :oauth_ You can now use the client to access the Xero API methods, e.g. +```ruby +client = Xeroizer::PublicApplication.new(YOUR_OAUTH_CONSUMER_KEY, YOUR_OAUTH_CONSUMER_SECRET).payroll +request_token = client.request_token(:oauth_callback => 'http://yourapp.com/oauth/callback') +redirect_to request_token.authorize_url(scope: Xeroizer::Scopes.all_payroll) +``` + +`Xeroizer::Scopes.all_payroll` requests access to all payroll endpoints. If you prefer, you can specify only specific endpoints: + +```ruby +redirect_to request_token.authorize_url(scope: 'payroll.employees,payroll.payitems') +``` + +#### Payroll Applications + +Payroll applications need to get permission to the appropriate API endpoints. Do this by providing a `scope` parameter when calling `request_token.authorize_url`. + ```ruby contacts = client.Contact.all ``` diff --git a/lib/xeroizer.rb b/lib/xeroizer.rb index 9b9c5333..991c4e39 100644 --- a/lib/xeroizer.rb +++ b/lib/xeroizer.rb @@ -17,6 +17,7 @@ require 'big_decimal_to_s' require 'class_level_inheritable_attributes' require 'xeroizer/oauth' +require 'xeroizer/scopes' require 'xeroizer/http_encoding_helper' require 'xeroizer/http' require 'xeroizer/exceptions' diff --git a/lib/xeroizer/payroll_application.rb b/lib/xeroizer/payroll_application.rb index 5e367b3d..91805907 100644 --- a/lib/xeroizer/payroll_application.rb +++ b/lib/xeroizer/payroll_application.rb @@ -3,6 +3,9 @@ class PayrollApplication attr_reader :application + extend Forwardable + def_delegators :application, :client, :request_token, :authorize_from_request, :authorize_from_access, :logger, :xero_url, :logger=, :xero_url=, :client, :rate_limit_sleep, :rate_limit_max_attempts + # Factory for new Payroll BaseModel instances with the class name `record_type`. # Only creates the instance if one doesn't already exist. # diff --git a/lib/xeroizer/scopes.rb b/lib/xeroizer/scopes.rb new file mode 100644 index 00000000..7baf7a3d --- /dev/null +++ b/lib/xeroizer/scopes.rb @@ -0,0 +1,7 @@ +module Xeroizer + class Scopes + def self.all_payroll + ['employees', 'payitems', 'leaveapplications', 'payrollcalendars', 'payruns', 'payslip', 'superfunds', 'superfundproducts', 'timesheets'].map {|s| "payroll.#{s}"}.join(',') + end + end +end \ No newline at end of file From 97ea6c6b7277e31d77e1d135275ae521487d3c04 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Sat, 1 Jun 2013 15:50:57 +1000 Subject: [PATCH 05/82] bug fixes --- lib/xeroizer/payroll_application.rb | 2 +- lib/xeroizer/scopes.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/xeroizer/payroll_application.rb b/lib/xeroizer/payroll_application.rb index 91805907..3d4aaead 100644 --- a/lib/xeroizer/payroll_application.rb +++ b/lib/xeroizer/payroll_application.rb @@ -4,7 +4,7 @@ class PayrollApplication attr_reader :application extend Forwardable - def_delegators :application, :client, :request_token, :authorize_from_request, :authorize_from_access, :logger, :xero_url, :logger=, :xero_url=, :client, :rate_limit_sleep, :rate_limit_max_attempts + def_delegators :application, :client, :request_token, :access_token, :authorize_from_request, :authorize_from_access, :logger, :xero_url, :logger=, :xero_url=, :client, :rate_limit_sleep, :rate_limit_max_attempts # Factory for new Payroll BaseModel instances with the class name `record_type`. # Only creates the instance if one doesn't already exist. diff --git a/lib/xeroizer/scopes.rb b/lib/xeroizer/scopes.rb index 7baf7a3d..6744a99a 100644 --- a/lib/xeroizer/scopes.rb +++ b/lib/xeroizer/scopes.rb @@ -1,7 +1,7 @@ module Xeroizer class Scopes def self.all_payroll - ['employees', 'payitems', 'leaveapplications', 'payrollcalendars', 'payruns', 'payslip', 'superfunds', 'superfundproducts', 'timesheets'].map {|s| "payroll.#{s}"}.join(',') + ['Employees', 'LeaveApplications', 'PayItems', 'PayrollCalendars', 'PayRuns', 'Payslip', 'SuperFunds', 'SuperFundProducts', 'Timesheets'].map {|s| "payroll.#{s.downcase}"}.join(',') end end end \ No newline at end of file From 698823ce2db44cc428c1be7504c36b835bd58155 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Wed, 5 Jun 2013 13:43:59 +1000 Subject: [PATCH 06/82] - add employee pay template fields - add ability to save (update/create) payroll models - uses post for both --- lib/xeroizer.rb | 3 +- lib/xeroizer/models/payroll/bank_account.rb | 2 +- lib/xeroizer/models/payroll/deduction_line.rb | 22 +++ lib/xeroizer/models/payroll/earnings_line.rb | 24 +++ lib/xeroizer/models/payroll/employee.rb | 2 + lib/xeroizer/models/payroll/leave_line.rb | 22 +++ lib/xeroizer/models/payroll/pay_template.rb | 26 ++++ .../models/payroll/reimbursement_line.rb | 21 +++ lib/xeroizer/models/payroll/super_line.rb | 25 +++ .../models/payroll/super_membership.rb | 20 +++ lib/xeroizer/models/payroll/timesheet_line.rb | 2 +- lib/xeroizer/record/base.rb | 14 +- lib/xeroizer/record/payroll_base.rb | 6 + lib/xeroizer/record/xml_helper.rb | 8 +- test/stub_responses/payroll_employees.xml | 91 ++++++++++- test/stub_responses/payroll_timesheets.xml | 69 ++++++++ ...e-007ebe02-4696-4453-a37d-fcca764a35a2.xml | 15 ++ ...e-33c0dccf-cc2d-44ef-97d7-64041ca914cc.xml | 22 +++ ...e-8a1618c6-306c-4f9b-b9cd-d637cc1b3da0.xml | 22 +++ ...e-de8599f6-04da-429a-a0cd-74c0da042c2b.xml | 22 +++ ...e-e8645dca-46ab-4026-a8d1-1c389378de50.xml | 22 +++ ...e-fb4ebd68-6568-41eb-96ab-628a0f54b4b8.xml | 147 ++++++++++++++++++ ...e-ff135c75-0121-48e3-8cac-128cfcc69b99.xml | 20 +++ ...t-049765fc-4506-48fb-bf88-3578dec0ec47.xml | 43 +++++ ...t-6ab1c708-a958-4788-8aa6-9a1d1a660900.xml | 30 ++++ test/unit/models/payroll/employees_test.rb | 37 +++++ test/unit/models/payroll/timesheet_test.rb | 20 +++ 27 files changed, 747 insertions(+), 10 deletions(-) create mode 100644 lib/xeroizer/models/payroll/deduction_line.rb create mode 100644 lib/xeroizer/models/payroll/earnings_line.rb create mode 100644 lib/xeroizer/models/payroll/leave_line.rb create mode 100644 lib/xeroizer/models/payroll/pay_template.rb create mode 100644 lib/xeroizer/models/payroll/reimbursement_line.rb create mode 100644 lib/xeroizer/models/payroll/super_line.rb create mode 100644 lib/xeroizer/models/payroll/super_membership.rb create mode 100644 test/stub_responses/payroll_timesheets.xml create mode 100644 test/stub_responses/records/payroll_employee-007ebe02-4696-4453-a37d-fcca764a35a2.xml create mode 100644 test/stub_responses/records/payroll_employee-33c0dccf-cc2d-44ef-97d7-64041ca914cc.xml create mode 100644 test/stub_responses/records/payroll_employee-8a1618c6-306c-4f9b-b9cd-d637cc1b3da0.xml create mode 100644 test/stub_responses/records/payroll_employee-de8599f6-04da-429a-a0cd-74c0da042c2b.xml create mode 100644 test/stub_responses/records/payroll_employee-e8645dca-46ab-4026-a8d1-1c389378de50.xml create mode 100644 test/stub_responses/records/payroll_employee-fb4ebd68-6568-41eb-96ab-628a0f54b4b8.xml create mode 100644 test/stub_responses/records/payroll_employee-ff135c75-0121-48e3-8cac-128cfcc69b99.xml create mode 100644 test/stub_responses/records/payroll_timesheet-049765fc-4506-48fb-bf88-3578dec0ec47.xml create mode 100644 test/stub_responses/records/payroll_timesheet-6ab1c708-a958-4788-8aa6-9a1d1a660900.xml create mode 100644 test/unit/models/payroll/employees_test.rb diff --git a/lib/xeroizer.rb b/lib/xeroizer.rb index 991c4e39..49a3ac52 100644 --- a/lib/xeroizer.rb +++ b/lib/xeroizer.rb @@ -42,7 +42,8 @@ # Include payroll models ['home_address', 'bank_account', 'employee', 'timesheet', 'timesheet_line', 'number_of_unit', 'leave_application', 'leave_period', 'pay_items', 'deduction_type', 'earnings_rate', - 'reimbursement_type', 'leave_type', 'payroll_calendar'].each do |payroll_model| + 'reimbursement_type', 'leave_type', 'payroll_calendar', 'pay_template', 'super_membership', + 'leave_line', 'reimbursement_line', 'super_line', 'deduction_line', 'earnings_line'].each do |payroll_model| require "xeroizer/models/payroll/#{payroll_model}" end diff --git a/lib/xeroizer/models/payroll/bank_account.rb b/lib/xeroizer/models/payroll/bank_account.rb index 4567d24a..71d5ce96 100644 --- a/lib/xeroizer/models/payroll/bank_account.rb +++ b/lib/xeroizer/models/payroll/bank_account.rb @@ -13,7 +13,7 @@ class BankAccount < PayrollBase string :bsb string :account_number boolean :remainder - string :percentage + decimal :percentage decimal :amount end diff --git a/lib/xeroizer/models/payroll/deduction_line.rb b/lib/xeroizer/models/payroll/deduction_line.rb new file mode 100644 index 00000000..d365917c --- /dev/null +++ b/lib/xeroizer/models/payroll/deduction_line.rb @@ -0,0 +1,22 @@ +module Xeroizer + module Record + module Payroll + + class DeductionLineModel < PayrollBaseModel + + end + + # child of PayTemplate + class DeductionLine < PayrollBase + + guid :deduction_type_id + string :calculation_type # http://developer.xero.com/payroll-api/types-and-codes#DeductionTypeCalculationType + + decimal :percentage + decimal :amount + + end + + end + end +end diff --git a/lib/xeroizer/models/payroll/earnings_line.rb b/lib/xeroizer/models/payroll/earnings_line.rb new file mode 100644 index 00000000..9d54cc64 --- /dev/null +++ b/lib/xeroizer/models/payroll/earnings_line.rb @@ -0,0 +1,24 @@ +module Xeroizer + module Record + module Payroll + + class EarningsLineModel < PayrollBaseModel + + end + + # child of PayTemplate + class EarningsLine < PayrollBase + + guid :earnings_rate_id + string :calculation_type # http://developer.xero.com/payroll-api/types-and-codes/#EarningsRateCalculationType + + decimal :number_of_units_per_week + decimal :annual_salary + decimal :rate_per_unit + decimal :normal_number_of_units + + end + + end + end +end diff --git a/lib/xeroizer/models/payroll/employee.rb b/lib/xeroizer/models/payroll/employee.rb index 66006e33..e4023651 100644 --- a/lib/xeroizer/models/payroll/employee.rb +++ b/lib/xeroizer/models/payroll/employee.rb @@ -37,6 +37,8 @@ class Employee < PayrollBase belongs_to :home_address, :internal_name_singular => "home_address", :model_name => "HomeAddress" has_many :bank_accounts + belongs_to :pay_template, :internal_name_singular => "pay_template", :model_name => "PayTemplate" + has_many :super_memberships end diff --git a/lib/xeroizer/models/payroll/leave_line.rb b/lib/xeroizer/models/payroll/leave_line.rb new file mode 100644 index 00000000..54149597 --- /dev/null +++ b/lib/xeroizer/models/payroll/leave_line.rb @@ -0,0 +1,22 @@ +module Xeroizer + module Record + module Payroll + + class LeaveLineModel < PayrollBaseModel + + end + + # child of PayTemplate + class LeaveLine < PayrollBase + + guid :leave_line_id + string :calculation_type # http://developer.xero.com/payroll-api/types-and-codes#LeaveTypeCalculationType + + decimal :annual_number_of_units + decimal :full_time_number_of_units_per_period + decimal :number_of_units + + end + end + end +end diff --git a/lib/xeroizer/models/payroll/pay_template.rb b/lib/xeroizer/models/payroll/pay_template.rb new file mode 100644 index 00000000..02fcb097 --- /dev/null +++ b/lib/xeroizer/models/payroll/pay_template.rb @@ -0,0 +1,26 @@ +module Xeroizer + module Record + module Payroll + + class PayTemplateModel < PayrollBaseModel + + set_all_children_are_subtypes true + set_xml_root_name 'PayTemplate' + set_xml_node_name 'PayTemplate' + end + + class PayTemplate < PayrollBase + + set_primary_key false + + has_many :earnings_lines + has_many :deduction_lines + has_many :super_lines + has_many :reimbursement_lines + has_many :leave_lines + + end + + end + end +end diff --git a/lib/xeroizer/models/payroll/reimbursement_line.rb b/lib/xeroizer/models/payroll/reimbursement_line.rb new file mode 100644 index 00000000..3a362bfe --- /dev/null +++ b/lib/xeroizer/models/payroll/reimbursement_line.rb @@ -0,0 +1,21 @@ +module Xeroizer + module Record + module Payroll + + class ReimbursementLineModel < PayrollBaseModel + + end + + # child of PayTemplate + class ReimbursementLine < PayrollBase + + guid :reimbursement_type_id + + string :description + decimal :amount + + end + + end + end +end diff --git a/lib/xeroizer/models/payroll/super_line.rb b/lib/xeroizer/models/payroll/super_line.rb new file mode 100644 index 00000000..31035c41 --- /dev/null +++ b/lib/xeroizer/models/payroll/super_line.rb @@ -0,0 +1,25 @@ +module Xeroizer + module Record + module Payroll + + class SuperLineModel < PayrollBaseModel + + end + + # child of PayTemplate + class SuperLine < PayrollBase + + guid :super_membership_id + string :contribution_type # http://developer.xero.com/payroll-api/types-and-codes#SuperannuationContributionType + string :calculation_type # http://developer.xero.com/payroll-api/types-and-codes#SuperannuationCalculationType + string :expense_account_code + string :liability_account_code + + decimal :minimum_monthly_earnings + decimal :percentage + + end + + end + end +end diff --git a/lib/xeroizer/models/payroll/super_membership.rb b/lib/xeroizer/models/payroll/super_membership.rb new file mode 100644 index 00000000..4b30edf3 --- /dev/null +++ b/lib/xeroizer/models/payroll/super_membership.rb @@ -0,0 +1,20 @@ +module Xeroizer + module Record + module Payroll + + class SuperMembershipModel < PayrollBaseModel + + end + + # child of Employee + class SuperMembership < PayrollBase + + guid :super_membership_id + guid :super_fund_id + string :employee_number + + end + + end + end +end diff --git a/lib/xeroizer/models/payroll/timesheet_line.rb b/lib/xeroizer/models/payroll/timesheet_line.rb index 5a9f664e..56e5d181 100644 --- a/lib/xeroizer/models/payroll/timesheet_line.rb +++ b/lib/xeroizer/models/payroll/timesheet_line.rb @@ -9,7 +9,7 @@ class TimesheetLineModel < PayrollBaseModel class TimesheetLine < PayrollBase guid :earnings_rate_id - guid :tracking_item_id # TODO: belongs to a http://developer.xero.com/api/Tracking%20Categories/ (optionally? the docs don't have any examples) + guid :tracking_item_id has_array :number_of_units, :api_child_name => 'NumberOfUnit' diff --git a/lib/xeroizer/record/base.rb b/lib/xeroizer/record/base.rb index e297cbdb..9acc63c9 100644 --- a/lib/xeroizer/record/base.rb +++ b/lib/xeroizer/record/base.rb @@ -16,6 +16,9 @@ class Base attr_reader :parent attr_accessor :errors attr_accessor :complete_record_downloaded + + attr_writer :api_method_for_creating + attr_writer :api_method_for_updating include ModelDefinitionHelper include RecordAssociationHelper @@ -125,13 +128,20 @@ def inspect end protected + + def api_method_for_creating + @api_method_for_creating || :http_put + end + def api_method_for_updating + @api_method_for_updating || :http_post + end # Attempt to create a new record. def create request = to_xml log "[CREATE SENT] (#{__FILE__}:#{__LINE__}) #{request}" - response = parent.http_put(request) + response = parent.send(api_method_for_creating, request) log "[CREATE RECEIVED] (#{__FILE__}:#{__LINE__}) #{response}" parse_save_response(response) @@ -147,7 +157,7 @@ def update log "[UPDATE SENT] (#{__FILE__}:#{__LINE__}) \r\n#{request}" - response = parent.http_post(request) + response = parent.send(api_method_for_updating, request) log "[UPDATE RECEIVED] (#{__FILE__}:#{__LINE__}) \r\n#{response}" diff --git a/lib/xeroizer/record/payroll_base.rb b/lib/xeroizer/record/payroll_base.rb index 89e5b350..dc3ed7f0 100644 --- a/lib/xeroizer/record/payroll_base.rb +++ b/lib/xeroizer/record/payroll_base.rb @@ -19,6 +19,12 @@ def self.has_many(field_name, options = {}) public + def initialize(parent) + super(parent) + self.api_method_for_creating = :http_post + self.api_method_for_updating = :http_post + end + def new_model_class(model_name) Xeroizer::Record::Payroll.const_get("#{model_name}Model".to_sym).new(parent.application, model_name.to_s) end diff --git a/lib/xeroizer/record/xml_helper.rb b/lib/xeroizer/record/xml_helper.rb index de373328..2d0246d2 100644 --- a/lib/xeroizer/record/xml_helper.rb +++ b/lib/xeroizer/record/xml_helper.rb @@ -45,7 +45,13 @@ def build_from_node(node, parent, base_module, all_children_are_subtypes = false end when :has_array - raise 'TODO' + if element.element_children.size > 0 + sub_field_name = field[:model_name] ? field[:model_name].to_sym : element.children.first.name.to_sym + sub_parent = record.new_model_class(sub_field_name) + element.element_children.inject([]) do |list, child| + list << base_module.const_get(sub_field_name).build_from_node(child, sub_parent, base_module) + end + end end if field[:calculated] diff --git a/test/stub_responses/payroll_employees.xml b/test/stub_responses/payroll_employees.xml index 77dbef09..ae8713c9 100644 --- a/test/stub_responses/payroll_employees.xml +++ b/test/stub_responses/payroll_employees.xml @@ -5,11 +5,94 @@ 2013-05-30T05:25:03.0599507Z - 07c60af4-5c86-44d9-8146-fab3733a087c + fb4ebd68-6568-41eb-96ab-628a0f54b4b8 + James + Lebron ACTIVE - Alex - Ghiculescu - 2013-04-18T01:29:53 + JL@madeup.email.com + 1978-08-13T00:00:00 + M + 0400-000-123 + 408-230-9732 + 2012-01-30T00:00:00 + 72e962d1-fcac-4083-8a71-742bb3e7ae14 + cb8e4706-2fdc-4170-aebd-0ffb855557f5 + 2013-04-01T23:02:36 + + + 007ebe02-4696-4453-a37d-fcca764a35a2 + John + Smith + ACTIVE + 2013-04-01T23:27:35 + + + de8599f6-04da-429a-a0cd-74c0da042c2b + Odette + Garrison + ACTIVE + ogg@madeup.email.com + 1975-05-18T00:00:00 + F + 9000 1234 + 2012-04-04T00:00:00 + 72e962d1-fcac-4083-8a71-742bb3e7ae14 + 97e0784f-6c3e-4371-a177-f93fbada3c58 + 2013-04-01T23:02:36 + + + 33c0dccf-cc2d-44ef-97d7-64041ca914cc + Oliver + Gray + ACTIVE + og@madeup.email.com + 1965-12-26T00:00:00 + M + 0401 123 456 + 2012-07-02T00:00:00 + 72e962d1-fcac-4083-8a71-742bb3e7ae14 + cb8e4706-2fdc-4170-aebd-0ffb855557f5 + 2013-04-01T23:02:36 + + + 8a1618c6-306c-4f9b-b9cd-d637cc1b3da0 + Sally + Martin + ACTIVE + sm@madeup3993.com + 1983-11-26T00:00:00 + F + 0400 123 456 + 2012-12-30T00:00:00 + 72e962d1-fcac-4083-8a71-742bb3e7ae14 + cb8e4706-2fdc-4170-aebd-0ffb855557f5 + 2013-04-01T23:02:36 + + + ff135c75-0121-48e3-8cac-128cfcc69b99 + Sonia + Michaels + ACTIVE + som@madeup3993.com + 1986-01-22T00:00:00 + F + 02 2345 6789 + + 2013-04-01T23:02:36 + + + e8645dca-46ab-4026-a8d1-1c389378de50 + Tracy + Green + ACTIVE + tg@madeup.email.com + 1985-08-04T00:00:00 + F + 0402 123 456 + 2012-05-01T00:00:00 + 72e962d1-fcac-4083-8a71-742bb3e7ae14 + 97e0784f-6c3e-4371-a177-f93fbada3c58 + 2013-04-01T23:02:36 \ No newline at end of file diff --git a/test/stub_responses/payroll_timesheets.xml b/test/stub_responses/payroll_timesheets.xml new file mode 100644 index 00000000..41b17f5d --- /dev/null +++ b/test/stub_responses/payroll_timesheets.xml @@ -0,0 +1,69 @@ + + a1b8088e-4166-48e9-9e29-5b5919cac0a0 + OK + Xeroizer test + 2013-05-30T05:25:03.0599507Z + + + 049765fc-4506-48fb-bf88-3578dec0ec47 + 72a0d0c2-0cf8-4f0b-ade1-33231f47b41b + 2013-02-01T00:00:00Z + 2013-02-07T00:00:00Z + Processed + 31.0000 + + + 966c5c77-2ef0-4320-b6a9-6c27b080ecc5 + + 7.00 + 6.00 + 6.00 + 5.00 + 0.00 + 0.00 + 0.00 + + 2013-03-21T20:14:28 + + + 677b6175-dc1c-4255-99e2-157117ae5bcc + + 3.00 + 4.00 + 0.00 + 0.00 + 0.00 + 0.00 + 0.00 + + 2013-03-21T20:14:28 + + + 2013-03-21T20:14:28 + + + 6ab1c708-a958-4788-8aa6-9a1d1a660900 + 72a0d0c2-0cf8-4f0b-ade1-33231f47b41b + 2013-02-08T00:00:00Z + 2013-02-14T00:00:00Z + Processed + 30.0000 + + + 966c5c77-2ef0-4320-b6a9-6c27b080ecc5 + + 8.00 + 8.00 + 8.00 + 6.00 + 0.00 + 0.00 + 0.00 + + 2013-03-21T20:14:28 + + + 2013-03-21T20:14:28 + + + \ No newline at end of file diff --git a/test/stub_responses/records/payroll_employee-007ebe02-4696-4453-a37d-fcca764a35a2.xml b/test/stub_responses/records/payroll_employee-007ebe02-4696-4453-a37d-fcca764a35a2.xml new file mode 100644 index 00000000..419db7b1 --- /dev/null +++ b/test/stub_responses/records/payroll_employee-007ebe02-4696-4453-a37d-fcca764a35a2.xml @@ -0,0 +1,15 @@ + + 007ebe02-4696-4453-a37d-fcca764a35a2 + OK + Demo AU + 2011-05-31T01:18:05.1105867Z + + + 007ebe02-4696-4453-a37d-fcca764a35a2 + John + Smith + ACTIVE + 2013-04-01T23:27:35 + + + \ No newline at end of file diff --git a/test/stub_responses/records/payroll_employee-33c0dccf-cc2d-44ef-97d7-64041ca914cc.xml b/test/stub_responses/records/payroll_employee-33c0dccf-cc2d-44ef-97d7-64041ca914cc.xml new file mode 100644 index 00000000..c9cce18a --- /dev/null +++ b/test/stub_responses/records/payroll_employee-33c0dccf-cc2d-44ef-97d7-64041ca914cc.xml @@ -0,0 +1,22 @@ + + 33c0dccf-cc2d-44ef-97d7-64041ca914cc + OK + Demo AU + 2011-05-31T01:18:05.1105867Z + + + 33c0dccf-cc2d-44ef-97d7-64041ca914cc + Oliver + Gray + ACTIVE + og@madeup.email.com + 1965-12-26T00:00:00 + M + 0401 123 456 + 2012-07-02T00:00:00 + 72e962d1-fcac-4083-8a71-742bb3e7ae14 + cb8e4706-2fdc-4170-aebd-0ffb855557f5 + 2013-04-01T23:02:36 + + + \ No newline at end of file diff --git a/test/stub_responses/records/payroll_employee-8a1618c6-306c-4f9b-b9cd-d637cc1b3da0.xml b/test/stub_responses/records/payroll_employee-8a1618c6-306c-4f9b-b9cd-d637cc1b3da0.xml new file mode 100644 index 00000000..d8a2fda7 --- /dev/null +++ b/test/stub_responses/records/payroll_employee-8a1618c6-306c-4f9b-b9cd-d637cc1b3da0.xml @@ -0,0 +1,22 @@ + + 8a1618c6-306c-4f9b-b9cd-d637cc1b3da0 + OK + Demo AU + 2011-05-31T01:18:05.1105867Z + + + 8a1618c6-306c-4f9b-b9cd-d637cc1b3da0 + Sally + Martin + ACTIVE + sm@madeup3993.com + 1983-11-26T00:00:00 + F + 0400 123 456 + 2012-12-30T00:00:00 + 72e962d1-fcac-4083-8a71-742bb3e7ae14 + cb8e4706-2fdc-4170-aebd-0ffb855557f5 + 2013-04-01T23:02:36 + + + \ No newline at end of file diff --git a/test/stub_responses/records/payroll_employee-de8599f6-04da-429a-a0cd-74c0da042c2b.xml b/test/stub_responses/records/payroll_employee-de8599f6-04da-429a-a0cd-74c0da042c2b.xml new file mode 100644 index 00000000..1ae0c97e --- /dev/null +++ b/test/stub_responses/records/payroll_employee-de8599f6-04da-429a-a0cd-74c0da042c2b.xml @@ -0,0 +1,22 @@ + + de8599f6-04da-429a-a0cd-74c0da042c2b + OK + Demo AU + 2011-05-31T01:18:05.1105867Z + + + de8599f6-04da-429a-a0cd-74c0da042c2b + Odette + Garrison + ACTIVE + ogg@madeup.email.com + 1975-05-18T00:00:00 + F + 9000 1234 + 2012-04-04T00:00:00 + 72e962d1-fcac-4083-8a71-742bb3e7ae14 + 97e0784f-6c3e-4371-a177-f93fbada3c58 + 2013-04-01T23:02:36 + + + \ No newline at end of file diff --git a/test/stub_responses/records/payroll_employee-e8645dca-46ab-4026-a8d1-1c389378de50.xml b/test/stub_responses/records/payroll_employee-e8645dca-46ab-4026-a8d1-1c389378de50.xml new file mode 100644 index 00000000..f37a4064 --- /dev/null +++ b/test/stub_responses/records/payroll_employee-e8645dca-46ab-4026-a8d1-1c389378de50.xml @@ -0,0 +1,22 @@ + + e8645dca-46ab-4026-a8d1-1c389378de50 + OK + Demo AU + 2011-05-31T01:18:05.1105867Z + + + e8645dca-46ab-4026-a8d1-1c389378de50 + Tracy + Green + ACTIVE + tg@madeup.email.com + 1985-08-04T00:00:00 + F + 0402 123 456 + 2012-05-01T00:00:00 + 72e962d1-fcac-4083-8a71-742bb3e7ae14 + 97e0784f-6c3e-4371-a177-f93fbada3c58 + 2013-04-01T23:02:36 + + + \ No newline at end of file diff --git a/test/stub_responses/records/payroll_employee-fb4ebd68-6568-41eb-96ab-628a0f54b4b8.xml b/test/stub_responses/records/payroll_employee-fb4ebd68-6568-41eb-96ab-628a0f54b4b8.xml new file mode 100644 index 00000000..08fa1ede --- /dev/null +++ b/test/stub_responses/records/payroll_employee-fb4ebd68-6568-41eb-96ab-628a0f54b4b8.xml @@ -0,0 +1,147 @@ + + fb4ebd68-6568-41eb-96ab-628a0f54b4b8 + OK + Demo AU + 2011-05-31T01:18:05.1105867Z + + + 07c60af4-5c86-44d9-8146-fab3733a087c + James + Lebron + ACTIVE + JL@madeup.email.com + 1978-08-13T00:00:00 + M + + 123 Main St + St. Kilda + VIC + 3182 + AUSTRALIA + + 0400-000-123 + 408-230-9732 + 2012-01-30T00:00:00 + 72e962d1-fcac-4083-8a71-742bb3e7ae14 + cb8e4706-2fdc-4170-aebd-0ffb855557f5 + 2013-04-01T23:02:36 + false + + false + true + true + false + false + false + 2013-04-26T00:04:48 + + + + Salary + James Lebron Savings + 122344 + 345678 + false + 20.0000 + + + Salary + James Lebron + 123443 + 2345678 + true + + + + 2012-07-01T00:00:00 + + + 72e962d1-fcac-4083-8a71-742bb3e7ae14 + 13333.33 + + + + + 59cd9d04-4521-4cc3-93ac-7841651ff407 + 40.00 + + + 4000.00 + + + 4333d5cd-53a5-4c31-98e5-a8b4e5676b0b + SGC + 3999.99 + + + + + + 742998cb-7584-4ecf-aa88-d694f59c50f9 + 100.0000 + + + 3bc7b584-a49f-40c6-ac5e-891f382785c9 + 40.0000 + + + + + + + 72e962d1-fcac-4083-8a71-742bb3e7ae14 + ANNUALSALARY + 40000.00 + 38.0000 + + + + + 59cd9d04-4521-4cc3-93ac-7841651ff407 + FIXEDAMOUNT + 10.00 + + + + + 4333d5cd-53a5-4c31-98e5-a8b4e5676b0b + SGC + PERCENTAGEOFEARNINGS + 450.00 + 478 + 826 + 9.0000 + + + 4333d5cd-53a5-4c31-98e5-a8b4e5676b0b + SALARYSACRIFICE + FIXEDAMOUNT + 478 + 826 + 50.0000 + + + + + + 742998cb-7584-4ecf-aa88-d694f59c50f9 + FIXEDAMOUNTEACHPERIOD + 152.0000 + + + 3bc7b584-a49f-40c6-ac5e-891f382785c9 + FIXEDAMOUNTEACHPERIOD + 76.0000 + + + + + + 4333d5cd-53a5-4c31-98e5-a8b4e5676b0b + 2187a42b-639a-45cb-9eed-cd4ae488306a + 1234 + + + + + \ No newline at end of file diff --git a/test/stub_responses/records/payroll_employee-ff135c75-0121-48e3-8cac-128cfcc69b99.xml b/test/stub_responses/records/payroll_employee-ff135c75-0121-48e3-8cac-128cfcc69b99.xml new file mode 100644 index 00000000..c9d5b6ef --- /dev/null +++ b/test/stub_responses/records/payroll_employee-ff135c75-0121-48e3-8cac-128cfcc69b99.xml @@ -0,0 +1,20 @@ + + ff135c75-0121-48e3-8cac-128cfcc69b99 + OK + Demo AU + 2011-05-31T01:18:05.1105867Z + + + ff135c75-0121-48e3-8cac-128cfcc69b99 + Sonia + Michaels + ACTIVE + som@madeup3993.com + 1986-01-22T00:00:00 + F + 02 2345 6789 + + 2013-04-01T23:02:36 + + + \ No newline at end of file diff --git a/test/stub_responses/records/payroll_timesheet-049765fc-4506-48fb-bf88-3578dec0ec47.xml b/test/stub_responses/records/payroll_timesheet-049765fc-4506-48fb-bf88-3578dec0ec47.xml new file mode 100644 index 00000000..ee513045 --- /dev/null +++ b/test/stub_responses/records/payroll_timesheet-049765fc-4506-48fb-bf88-3578dec0ec47.xml @@ -0,0 +1,43 @@ + + 049765fc-4506-48fb-bf88-3578dec0ec47 + OK + Xeroizer test + 2013-05-30T05:25:03.0599507Z + + 049765fc-4506-48fb-bf88-3578dec0ec47 + 72a0d0c2-0cf8-4f0b-ade1-33231f47b41b + 2013-02-01T00:00:00 + 2013-02-07T00:00:00 + Processed + 31.0000 + + + 966c5c77-2ef0-4320-b6a9-6c27b080ecc5 + + 7.00 + 6.00 + 6.00 + 5.00 + 0.00 + 0.00 + 0.00 + + 2013-03-21T20:14:28 + + + 677b6175-dc1c-4255-99e2-157117ae5bcc + + 3.00 + 4.00 + 0.00 + 0.00 + 0.00 + 0.00 + 0.00 + + 2013-03-21T20:14:28 + + + 2013-03-21T20:14:28 + + \ No newline at end of file diff --git a/test/stub_responses/records/payroll_timesheet-6ab1c708-a958-4788-8aa6-9a1d1a660900.xml b/test/stub_responses/records/payroll_timesheet-6ab1c708-a958-4788-8aa6-9a1d1a660900.xml new file mode 100644 index 00000000..0d787329 --- /dev/null +++ b/test/stub_responses/records/payroll_timesheet-6ab1c708-a958-4788-8aa6-9a1d1a660900.xml @@ -0,0 +1,30 @@ + + 6ab1c708-a958-4788-8aa6-9a1d1a660900 + OK + Xeroizer test + 2013-05-30T05:25:03.0599507Z + + 6ab1c708-a958-4788-8aa6-9a1d1a660900 + 72a0d0c2-0cf8-4f0b-ade1-33231f47b41b + 2013-02-08T00:00:00Z + 2013-02-14T00:00:00Z + Processed + 30.0000 + + + 966c5c77-2ef0-4320-b6a9-6c27b080ecc5 + + 8.00 + 8.00 + 8.00 + 6.00 + 0.00 + 0.00 + 0.00 + + 2013-03-21T20:14:28 + + + 2013-03-21T20:14:28 + + \ No newline at end of file diff --git a/test/unit/models/payroll/employees_test.rb b/test/unit/models/payroll/employees_test.rb new file mode 100644 index 00000000..609c64f7 --- /dev/null +++ b/test/unit/models/payroll/employees_test.rb @@ -0,0 +1,37 @@ +require 'test_helper' + +class EmployeesTest < Test::Unit::TestCase + include TestHelper + + def setup + @client = Xeroizer::PublicApplication.new(CONSUMER_KEY, CONSUMER_SECRET).payroll + mock_api('Employees') + end + + test "get all" do + employees = @client.Employee.all + assert_equal 7, employees.length + + employee = @client.Employee.find(employees.first.id) + assert_not_nil employee.home_address + assert_equal 2, employee.bank_accounts.length + assert_equal 1, employee.super_memberships.length + + assert_not_nil employee.pay_template + assert_equal 1, employee.pay_template.earnings_lines.length + +=begin + pay_items = @client.PayItem.first + assert_equal 4, pay_items.earnings_rates.size + assert_equal 5, pay_items.deduction_types.size + assert_equal 2, pay_items.reimbursement_types.size + assert_equal 9, pay_items.leave_types.size + + doc = Nokogiri::XML pay_items.to_xml + assert_equal 4, doc.xpath("/PayItems/EarningsRates/EarningsRate").size + assert_equal 5, doc.xpath("/PayItems/DeductionTypes/DeductionType").size + assert_equal 2, doc.xpath("/PayItems/ReimbursementTypes/ReimbursementType").size + assert_equal 9, doc.xpath("/PayItems/LeaveTypes/LeaveType").size +=end + end +end diff --git a/test/unit/models/payroll/timesheet_test.rb b/test/unit/models/payroll/timesheet_test.rb index b9da6f9b..2b42b32f 100644 --- a/test/unit/models/payroll/timesheet_test.rb +++ b/test/unit/models/payroll/timesheet_test.rb @@ -6,9 +6,29 @@ class TimesheetTest < Test::Unit::TestCase def setup @client = Xeroizer::PublicApplication.new(CONSUMER_KEY, CONSUMER_SECRET).payroll mock_api('Employees') + mock_api('Timesheets') @employee = @client.Employee.first end + test "get all" do + timesheets = @client.Timesheet.all + assert_equal 2, timesheets.count + timesheets.each do |timesheet| + timesheet.timesheet_lines.each do |line| + assert_equal 7, line.number_of_units.count + line.number_of_units.each do |unit| + assert_not_nil unit.value + end + end + end + end + + test "get one" do + timesheet = @client.Timesheet.first + assert_equal 2, timesheet.timesheet_lines.count + assert_equal 7, timesheet.timesheet_lines.sample.number_of_units.count + end + test "create invalid timesheet" do timesheet = @client.Timesheet.build assert_equal(false, timesheet.valid?) From 79db6c5ec0ca64bce9af832d46b5cd84f98c6379 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Thu, 6 Jun 2013 10:37:30 +1000 Subject: [PATCH 07/82] you dont need to request permission for superfundproducts --- lib/xeroizer/scopes.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/xeroizer/scopes.rb b/lib/xeroizer/scopes.rb index 6744a99a..2c5c0c44 100644 --- a/lib/xeroizer/scopes.rb +++ b/lib/xeroizer/scopes.rb @@ -1,7 +1,7 @@ module Xeroizer class Scopes def self.all_payroll - ['Employees', 'LeaveApplications', 'PayItems', 'PayrollCalendars', 'PayRuns', 'Payslip', 'SuperFunds', 'SuperFundProducts', 'Timesheets'].map {|s| "payroll.#{s.downcase}"}.join(',') + ['Employees', 'LeaveApplications', 'PayItems', 'PayrollCalendars', 'PayRuns', 'Payslip', 'SuperFunds', 'Timesheets'].map {|s| "payroll.#{s.downcase}"}.join(',') end end end \ No newline at end of file From 0ec7251e2f1df1b0af5390cafa024dd37793a394 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Thu, 6 Jun 2013 23:02:05 +1000 Subject: [PATCH 08/82] add page param, used by payroll api --- lib/xeroizer/record/base_model_http_proxy.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/xeroizer/record/base_model_http_proxy.rb b/lib/xeroizer/record/base_model_http_proxy.rb index d7db3fe1..cc119a00 100644 --- a/lib/xeroizer/record/base_model_http_proxy.rb +++ b/lib/xeroizer/record/base_model_http_proxy.rb @@ -26,6 +26,7 @@ def parse_params(options) end end params[:offset] = options[:offset] if options[:offset] + params[:page] = options[:page] if options[:page] params end From edeb07c84a957966f6f3c925bbe6e18c97b5099e Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Sun, 9 Jun 2013 13:36:36 +1000 Subject: [PATCH 09/82] fix bugs in using dates in where finders --- lib/xeroizer/record/base_model_http_proxy.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/xeroizer/record/base_model_http_proxy.rb b/lib/xeroizer/record/base_model_http_proxy.rb index cc119a00..e9277e56 100644 --- a/lib/xeroizer/record/base_model_http_proxy.rb +++ b/lib/xeroizer/record/base_model_http_proxy.rb @@ -105,8 +105,8 @@ def where_condition_part(field, expression, value) when :boolean then [field[:api_name], expression, value ? 'true' : 'false'] when :integer then [field[:api_name], expression, value.to_s] when :decimal then [field[:api_name], expression, value.to_s] - when :date then [field[:api_name], expression, "DateTime.Parse(\"#{value.strftime("%Y-%m-%d")}\")"] - when :datetime then [field[:api_name], expression, "DateTime.Parse(\"#{value.utc.strftime("%Y-%m-%dT%H:%M:%S")}\")"] + when :date then [field[:api_name], expression, "DateTime(#{value.strftime("%Y,%m,%d")})"] + when :datetime then [field[:api_name], expression, "DateTime(#{value.utc.strftime("%Y,%m,%d,%H,%M,%S")})"] when :belongs_to then when :has_many then end From b6a475623b0df305ffcab022b9e1487384ba678e Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Mon, 10 Jun 2013 23:19:44 +1000 Subject: [PATCH 10/82] add missing rate per unit field --- lib/xeroizer/models/payroll/earnings_rate.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/xeroizer/models/payroll/earnings_rate.rb b/lib/xeroizer/models/payroll/earnings_rate.rb index 43865afb..77089bad 100644 --- a/lib/xeroizer/models/payroll/earnings_rate.rb +++ b/lib/xeroizer/models/payroll/earnings_rate.rb @@ -17,6 +17,7 @@ class EarningsRate < PayrollBase guid :earnings_rate_id string :rate_type # http://developer.xero.com/payroll-api/types-and-codes/#EarningsRateTypes + decimal :rate_per_unit decimal :multiplier boolean :accrue_leave decimal :amount From 73593fc7e4dfe3b09e7949c4b26828a676e5f598 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Fri, 21 Jun 2013 11:55:13 +1000 Subject: [PATCH 11/82] update urls for partner app --- README.md | 2 ++ lib/xeroizer/generic_application.rb | 12 +++++++++--- lib/xeroizer/partner_application.rb | 2 +- lib/xeroizer/payroll_application.rb | 5 ++++- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index dffbcc67..248f6de5 100644 --- a/README.md +++ b/README.md @@ -402,6 +402,8 @@ in the resulting response, including all nested XML elements. contacts = xero.Contact.all(:where => 'Name.StartsWith("Pet")') contacts = xero.Contact.all(:where => 'Name.EndsWith("er")') +See [Xero's documentation on filters](http://developer.xero.com/api-overview/http-requests-and-responses/#get-filtered) for more information. + Associations ------------ diff --git a/lib/xeroizer/generic_application.rb b/lib/xeroizer/generic_application.rb index 5f5b7122..2a2e7d53 100644 --- a/lib/xeroizer/generic_application.rb +++ b/lib/xeroizer/generic_application.rb @@ -7,7 +7,8 @@ class GenericApplication extend Record::ApplicationHelper attr_reader :client, :rate_limit_sleep, :rate_limit_max_attempts - attr_accessor :logger, :xero_url + attr_writer :xero_url_prefix, :xero_url_suffix + attr_accessor :logger extend Forwardable def_delegators :client, :access_token @@ -45,7 +46,8 @@ class GenericApplication # @see PrivateApplication # @see PartnerApplication def initialize(consumer_key, consumer_secret, options = {}) - @xero_url = options[:xero_url] || "https://api.xero.com/api.xro/2.0" + @xero_url_prefix = options[:xero_url_prefix] || "https://api.xero.com/" + @xero_url_suffix = options[:xero_url_suffix] || "api.xro/2.0" @rate_limit_sleep = options[:rate_limit_sleep] || false @rate_limit_max_attempts = options[:rate_limit_max_attempts] || 5 @client = OAuth.new(consumer_key, consumer_secret, options) @@ -53,9 +55,13 @@ def initialize(consumer_key, consumer_secret, options = {}) def payroll(options = {}) xero_client = self.clone - xero_client.xero_url = options[:xero_url] || "https://api.xero.com/payroll.xro/1.0" + xero_client.xero_url_suffix = options[:xero_url_suffix] || "payroll.xro/1.0" @payroll ||= PayrollApplication.new(xero_client) end + + def xero_url + @xero_url_prefix + '/' + @xero_url_suffix + end end end diff --git a/lib/xeroizer/partner_application.rb b/lib/xeroizer/partner_application.rb index c27ec367..264c87a3 100644 --- a/lib/xeroizer/partner_application.rb +++ b/lib/xeroizer/partner_application.rb @@ -19,7 +19,7 @@ class PartnerApplication < GenericApplication # @return [PartnerApplication] instance of PrivateApplication def initialize(consumer_key, consumer_secret, path_to_private_key, path_to_ssl_client_cert, path_to_ssl_client_key, options = {}) default_options = { - :xero_url => 'https://api-partner.network.xero.com/api.xro/2.0', + :xero_url_prefix => 'https://api-partner.network.xero.com', :site => 'https://api-partner.network.xero.com', :authorize_url => 'https://api.xero.com/oauth/Authorize', :signature_method => 'RSA-SHA1' diff --git a/lib/xeroizer/payroll_application.rb b/lib/xeroizer/payroll_application.rb index 3d4aaead..4e63acd6 100644 --- a/lib/xeroizer/payroll_application.rb +++ b/lib/xeroizer/payroll_application.rb @@ -4,7 +4,10 @@ class PayrollApplication attr_reader :application extend Forwardable - def_delegators :application, :client, :request_token, :access_token, :authorize_from_request, :authorize_from_access, :logger, :xero_url, :logger=, :xero_url=, :client, :rate_limit_sleep, :rate_limit_max_attempts + def_delegators :application, :client, :request_token, :access_token, :authorize_from_request, + :authorize_from_access, :logger, :xero_url, :logger=, :xero_url=, :client, + :rate_limit_sleep, :rate_limit_max_attempts, # all from generic_application.rb + :renew_access_token, :expires_at, :authorization_expires_at, :session_handle # partner_application.rb # Factory for new Payroll BaseModel instances with the class name `record_type`. # Only creates the instance if one doesn't already exist. From 8fad650b973c6f0ab7a59f59a1031b2924cc9b06 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Wed, 7 Aug 2013 09:57:54 +1000 Subject: [PATCH 12/82] use the appropriate post/put action when doing a batch save --- lib/xeroizer/record/base.rb | 4 ++-- lib/xeroizer/record/base_model.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/xeroizer/record/base.rb b/lib/xeroizer/record/base.rb index 9acc63c9..f9b7d583 100644 --- a/lib/xeroizer/record/base.rb +++ b/lib/xeroizer/record/base.rb @@ -126,8 +126,6 @@ def inspect end.join(", ") "#<#{self.class} #{attribute_string}>" end - - protected def api_method_for_creating @api_method_for_creating || :http_put @@ -135,6 +133,8 @@ def api_method_for_creating def api_method_for_updating @api_method_for_updating || :http_post end + + protected # Attempt to create a new record. def create diff --git a/lib/xeroizer/record/base_model.rb b/lib/xeroizer/record/base_model.rb index bcade227..10b9d732 100644 --- a/lib/xeroizer/record/base_model.rb +++ b/lib/xeroizer/record/base_model.rb @@ -156,7 +156,7 @@ def batch_save if @objects[model_class] objects = @objects[model_class].values.compact return false unless objects.all?(&:valid?) - actions = objects.group_by {|o| o.new_record? ? :http_put : :http_post } + actions = objects.group_by {|o| o.new_record? ? o.api_method_for_creating : o.api_method_for_updating } actions.each_pair do |http_method, records| request = to_bulk_xml(records) response = parse_response(self.send(http_method, request, {:summarizeErrors => false})) From 795d6e8ef86f86f466937cfbea46d0d3c68fa746 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Wed, 7 Aug 2013 10:01:06 +1000 Subject: [PATCH 13/82] fix incorrectly ordered info --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 248f6de5..dd6ae22f 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,14 @@ client.authorize_from_request(request_token.token, request_token.secret, :oauth_ You can now use the client to access the Xero API methods, e.g. +```ruby +contacts = client.Contact.all +``` + +#### Payroll Applications + +Payroll applications need to get permission to the appropriate API endpoints. Do this by providing a `scope` parameter when calling `request_token.authorize_url`. + ```ruby client = Xeroizer::PublicApplication.new(YOUR_OAUTH_CONSUMER_KEY, YOUR_OAUTH_CONSUMER_SECRET).payroll request_token = client.request_token(:oauth_callback => 'http://yourapp.com/oauth/callback') @@ -110,14 +118,6 @@ redirect_to request_token.authorize_url(scope: Xeroizer::Scopes.all_payroll) ```ruby redirect_to request_token.authorize_url(scope: 'payroll.employees,payroll.payitems') ``` - -#### Payroll Applications - -Payroll applications need to get permission to the appropriate API endpoints. Do this by providing a `scope` parameter when calling `request_token.authorize_url`. - -```ruby -contacts = client.Contact.all -``` #### Example Rails Controller From ef4935990978320c6a6420310a7ca837a4d88f59 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Wed, 7 Aug 2013 10:17:36 +1000 Subject: [PATCH 14/82] add opening_balances (child of Employee) --- lib/xeroizer.rb | 2 +- lib/xeroizer/models/payroll/employee.rb | 1 + .../models/payroll/opening_balance.rb | 28 +++++++++++++++++++ test/unit/models/payroll/employees_test.rb | 16 ++--------- 4 files changed, 33 insertions(+), 14 deletions(-) create mode 100644 lib/xeroizer/models/payroll/opening_balance.rb diff --git a/lib/xeroizer.rb b/lib/xeroizer.rb index 49a3ac52..a328b00a 100644 --- a/lib/xeroizer.rb +++ b/lib/xeroizer.rb @@ -43,7 +43,7 @@ ['home_address', 'bank_account', 'employee', 'timesheet', 'timesheet_line', 'number_of_unit', 'leave_application', 'leave_period', 'pay_items', 'deduction_type', 'earnings_rate', 'reimbursement_type', 'leave_type', 'payroll_calendar', 'pay_template', 'super_membership', - 'leave_line', 'reimbursement_line', 'super_line', 'deduction_line', 'earnings_line'].each do |payroll_model| + 'leave_line', 'reimbursement_line', 'super_line', 'deduction_line', 'earnings_line', 'opening_balance'].each do |payroll_model| require "xeroizer/models/payroll/#{payroll_model}" end diff --git a/lib/xeroizer/models/payroll/employee.rb b/lib/xeroizer/models/payroll/employee.rb index e4023651..f965c4e9 100644 --- a/lib/xeroizer/models/payroll/employee.rb +++ b/lib/xeroizer/models/payroll/employee.rb @@ -38,6 +38,7 @@ class Employee < PayrollBase belongs_to :home_address, :internal_name_singular => "home_address", :model_name => "HomeAddress" has_many :bank_accounts belongs_to :pay_template, :internal_name_singular => "pay_template", :model_name => "PayTemplate" + belongs_to :opening_balances, :internal_name_singular => "opening_balance", :model_name => "OpeningBalances" has_many :super_memberships end diff --git a/lib/xeroizer/models/payroll/opening_balance.rb b/lib/xeroizer/models/payroll/opening_balance.rb new file mode 100644 index 00000000..f0691083 --- /dev/null +++ b/lib/xeroizer/models/payroll/opening_balance.rb @@ -0,0 +1,28 @@ +module Xeroizer + module Record + module Payroll + + class OpeningBalancesModel < PayrollBaseModel + + set_all_children_are_subtypes true + set_xml_root_name 'OpeningBalances' + set_xml_node_name 'OpeningBalances' + end + + # child of Employee + class OpeningBalances < PayrollBase + + set_primary_key false + + date :opening_balance_date + has_many :earnings_lines + has_many :deduction_lines + has_many :super_lines + has_many :reimbursement_lines + has_many :leave_lines + + end + + end + end +end diff --git a/test/unit/models/payroll/employees_test.rb b/test/unit/models/payroll/employees_test.rb index 609c64f7..3e21ffe1 100644 --- a/test/unit/models/payroll/employees_test.rb +++ b/test/unit/models/payroll/employees_test.rb @@ -20,18 +20,8 @@ def setup assert_not_nil employee.pay_template assert_equal 1, employee.pay_template.earnings_lines.length -=begin - pay_items = @client.PayItem.first - assert_equal 4, pay_items.earnings_rates.size - assert_equal 5, pay_items.deduction_types.size - assert_equal 2, pay_items.reimbursement_types.size - assert_equal 9, pay_items.leave_types.size - - doc = Nokogiri::XML pay_items.to_xml - assert_equal 4, doc.xpath("/PayItems/EarningsRates/EarningsRate").size - assert_equal 5, doc.xpath("/PayItems/DeductionTypes/DeductionType").size - assert_equal 2, doc.xpath("/PayItems/ReimbursementTypes/ReimbursementType").size - assert_equal 9, doc.xpath("/PayItems/LeaveTypes/LeaveType").size -=end + assert_not_nil employee.opening_balances + assert_not_nil employee.opening_balances.opening_balance_date + assert_equal 1, employee.opening_balances.earnings_lines.length end end From de93783009a8e91c1d803c5c2212bb549e0c0669 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Sat, 12 Oct 2013 22:54:18 +1000 Subject: [PATCH 15/82] add settings scope --- lib/xeroizer/scopes.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/xeroizer/scopes.rb b/lib/xeroizer/scopes.rb index 2c5c0c44..8d024a28 100644 --- a/lib/xeroizer/scopes.rb +++ b/lib/xeroizer/scopes.rb @@ -1,7 +1,7 @@ module Xeroizer class Scopes def self.all_payroll - ['Employees', 'LeaveApplications', 'PayItems', 'PayrollCalendars', 'PayRuns', 'Payslip', 'SuperFunds', 'Timesheets'].map {|s| "payroll.#{s.downcase}"}.join(',') + ['Employees', 'LeaveApplications', 'PayItems', 'PayrollCalendars', 'PayRuns', 'Payslip', 'SuperFunds', 'Settings', 'Timesheets'].map {|s| "payroll.#{s.downcase}"}.join(',') end end end \ No newline at end of file From cbc529fa73a67536499c69d12148a096348e0bc5 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Sun, 13 Oct 2013 11:03:28 +1000 Subject: [PATCH 16/82] add settings, pay runs, payslips --- lib/xeroizer.rb | 3 +- lib/xeroizer/generic_application.rb | 2 +- lib/xeroizer/models/payroll/account.rb | 21 +++ .../models/payroll/employee_groups.rb | 24 +++ .../models/payroll/leave_application.rb | 2 - .../models/payroll/opening_balance.rb | 2 +- lib/xeroizer/models/payroll/pay_items.rb | 2 +- lib/xeroizer/models/payroll/pay_run.rb | 40 +++++ lib/xeroizer/models/payroll/pay_template.rb | 2 +- lib/xeroizer/models/payroll/payslip.rb | 31 ++++ lib/xeroizer/models/payroll/settings.rb | 27 ++++ lib/xeroizer/models/payroll/super_line.rb | 3 + .../models/payroll/timesheet_categories.rb | 24 +++ .../models/payroll/tracking_categories.rb | 26 ++++ lib/xeroizer/payroll_application.rb | 3 + lib/xeroizer/record/base_model.rb | 28 ++-- .../record/record_association_helper.rb | 1 + lib/xeroizer/record/xml_helper.rb | 10 +- privatekey.pem | 15 ++ public_privatekey.pfx | Bin 0 -> 1885 bytes publickey.cer | 21 +++ test/dummy_script.rb | 26 ++++ .../payroll_leave_applications.xml | 6 +- test/stub_responses/payroll_pay_runs.xml | 34 ++++ ...e-07c60af4-5c86-44d9-8146-fab3733a087c.xml | 147 ------------------ ...e-fb4ebd68-6568-41eb-96ab-628a0f54b4b8.xml | 2 +- ...-fb4ebd68-6568-41eb-96ab-628a0f54b4b8.xml} | 2 +- ...n-7c998e04-1cee-4a19-bfe6-3cbfd5cb9cea.xml | 62 ++++++++ ...n-e3bdb2f7-2b20-45e6-ac8d-ec67d17de9f4.xml | 62 ++++++++ test/test_helper.rb | 23 ++- .../models/payroll/leave_application_test.rb | 8 +- test/unit/models/payroll/pay_runs_test.rb | 15 ++ 32 files changed, 494 insertions(+), 180 deletions(-) create mode 100644 lib/xeroizer/models/payroll/account.rb create mode 100644 lib/xeroizer/models/payroll/employee_groups.rb create mode 100644 lib/xeroizer/models/payroll/pay_run.rb create mode 100644 lib/xeroizer/models/payroll/payslip.rb create mode 100644 lib/xeroizer/models/payroll/settings.rb create mode 100644 lib/xeroizer/models/payroll/timesheet_categories.rb create mode 100644 lib/xeroizer/models/payroll/tracking_categories.rb create mode 100644 privatekey.pem create mode 100644 public_privatekey.pfx create mode 100644 publickey.cer create mode 100644 test/dummy_script.rb create mode 100644 test/stub_responses/payroll_pay_runs.xml delete mode 100644 test/stub_responses/records/payroll_employee-07c60af4-5c86-44d9-8146-fab3733a087c.xml rename test/stub_responses/records/{payroll_leave_application-07c60af4-5c86-44d9-8146-fab3733a087c.xml => payroll_leave_application-fb4ebd68-6568-41eb-96ab-628a0f54b4b8.xml} (94%) create mode 100644 test/stub_responses/records/payroll_payroll_run-7c998e04-1cee-4a19-bfe6-3cbfd5cb9cea.xml create mode 100644 test/stub_responses/records/payroll_payroll_run-e3bdb2f7-2b20-45e6-ac8d-ec67d17de9f4.xml create mode 100644 test/unit/models/payroll/pay_runs_test.rb diff --git a/lib/xeroizer.rb b/lib/xeroizer.rb index a328b00a..a8dcdeb7 100644 --- a/lib/xeroizer.rb +++ b/lib/xeroizer.rb @@ -43,7 +43,8 @@ ['home_address', 'bank_account', 'employee', 'timesheet', 'timesheet_line', 'number_of_unit', 'leave_application', 'leave_period', 'pay_items', 'deduction_type', 'earnings_rate', 'reimbursement_type', 'leave_type', 'payroll_calendar', 'pay_template', 'super_membership', - 'leave_line', 'reimbursement_line', 'super_line', 'deduction_line', 'earnings_line', 'opening_balance'].each do |payroll_model| + 'leave_line', 'reimbursement_line', 'super_line', 'deduction_line', 'earnings_line', 'opening_balance', + 'pay_run', 'settings', 'tracking_categories', 'employee_groups', 'timesheet_categories', 'account'].each do |payroll_model| require "xeroizer/models/payroll/#{payroll_model}" end diff --git a/lib/xeroizer/generic_application.rb b/lib/xeroizer/generic_application.rb index 2a2e7d53..bd7921e8 100644 --- a/lib/xeroizer/generic_application.rb +++ b/lib/xeroizer/generic_application.rb @@ -46,7 +46,7 @@ class GenericApplication # @see PrivateApplication # @see PartnerApplication def initialize(consumer_key, consumer_secret, options = {}) - @xero_url_prefix = options[:xero_url_prefix] || "https://api.xero.com/" + @xero_url_prefix = options[:xero_url_prefix] || "https://api.xero.com" @xero_url_suffix = options[:xero_url_suffix] || "api.xro/2.0" @rate_limit_sleep = options[:rate_limit_sleep] || false @rate_limit_max_attempts = options[:rate_limit_max_attempts] || 5 diff --git a/lib/xeroizer/models/payroll/account.rb b/lib/xeroizer/models/payroll/account.rb new file mode 100644 index 00000000..d377fc95 --- /dev/null +++ b/lib/xeroizer/models/payroll/account.rb @@ -0,0 +1,21 @@ +module Xeroizer + module Record + module Payroll + + class AccountModel < PayrollBaseModel + + set_permissions :read + + end + + class Account < PayrollBase + + string :type + string :code + string :name + guid :account_id + end + + end + end +end diff --git a/lib/xeroizer/models/payroll/employee_groups.rb b/lib/xeroizer/models/payroll/employee_groups.rb new file mode 100644 index 00000000..a4b5397f --- /dev/null +++ b/lib/xeroizer/models/payroll/employee_groups.rb @@ -0,0 +1,24 @@ +module Xeroizer + module Record + module Payroll + + class EmployeeGroupsModel < PayrollBaseModel + + set_standalone_model true + set_xml_root_name 'EmployeeGroups' + set_xml_node_name 'EmployeeGroups' + end + + # child of TrackingCategories + class EmployeeGroups < PayrollBase + + set_primary_key false + + guid :tracking_category_id + string :tracking_category_name + + end + + end + end +end diff --git a/lib/xeroizer/models/payroll/leave_application.rb b/lib/xeroizer/models/payroll/leave_application.rb index 060b4500..de1b0b34 100644 --- a/lib/xeroizer/models/payroll/leave_application.rb +++ b/lib/xeroizer/models/payroll/leave_application.rb @@ -5,8 +5,6 @@ module Payroll class LeaveApplicationModel < PayrollBaseModel set_permissions :read, :write, :update - - # TODO: calling #get on this requries an employee ID end class LeaveApplication < PayrollBase diff --git a/lib/xeroizer/models/payroll/opening_balance.rb b/lib/xeroizer/models/payroll/opening_balance.rb index f0691083..90fb7fb2 100644 --- a/lib/xeroizer/models/payroll/opening_balance.rb +++ b/lib/xeroizer/models/payroll/opening_balance.rb @@ -4,7 +4,7 @@ module Payroll class OpeningBalancesModel < PayrollBaseModel - set_all_children_are_subtypes true + set_standalone_model true set_xml_root_name 'OpeningBalances' set_xml_node_name 'OpeningBalances' end diff --git a/lib/xeroizer/models/payroll/pay_items.rb b/lib/xeroizer/models/payroll/pay_items.rb index 12ed7345..d1a27296 100644 --- a/lib/xeroizer/models/payroll/pay_items.rb +++ b/lib/xeroizer/models/payroll/pay_items.rb @@ -6,7 +6,7 @@ class PayItemModel < PayrollBaseModel set_permissions :read, :write, :update - set_all_children_are_subtypes true + set_standalone_model true set_xml_root_name 'PayItems' set_xml_node_name 'PayItems' end diff --git a/lib/xeroizer/models/payroll/pay_run.rb b/lib/xeroizer/models/payroll/pay_run.rb new file mode 100644 index 00000000..2953346c --- /dev/null +++ b/lib/xeroizer/models/payroll/pay_run.rb @@ -0,0 +1,40 @@ +module Xeroizer + module Record + module Payroll + + class PayRunModel < PayrollBaseModel + + set_permissions :read, :write, :update + + end + + # http://developer.xero.com/documentation/payroll-api/payruns/ + class PayRun < PayrollBase + + set_primary_key :pay_run_id + + guid :pay_run_id + + guid :payroll_calendar_id + + date :pay_run_period_end_date + date :pay_run_period_start_date + date :payment_date + + decimal :wages + decimal :deductions + decimal :tax + decimal :super + decimal :reimbursement + decimal :net_pay + + string :pay_run_status + string :payslip_message + + #has_many :payslips + + end + + end + end +end diff --git a/lib/xeroizer/models/payroll/pay_template.rb b/lib/xeroizer/models/payroll/pay_template.rb index 02fcb097..ce70dc2d 100644 --- a/lib/xeroizer/models/payroll/pay_template.rb +++ b/lib/xeroizer/models/payroll/pay_template.rb @@ -4,7 +4,7 @@ module Payroll class PayTemplateModel < PayrollBaseModel - set_all_children_are_subtypes true + set_standalone_model true set_xml_root_name 'PayTemplate' set_xml_node_name 'PayTemplate' end diff --git a/lib/xeroizer/models/payroll/payslip.rb b/lib/xeroizer/models/payroll/payslip.rb new file mode 100644 index 00000000..155c864e --- /dev/null +++ b/lib/xeroizer/models/payroll/payslip.rb @@ -0,0 +1,31 @@ +module Xeroizer + module Record + module Payroll + + class PayslipModel < PayrollBaseModel + + set_permissions :read, :update + + end + + # http://developer.xero.com/documentation/payroll-api/payslip/ + class Payslip < PayrollBase + + set_primary_key :payslip_id + + guid :payslip_id + + guid :employee_id + + has_many :earnings_lines + #has_many :timesheet_earnings_lines # TODO + has_many :deduction_lines + #has_many :leave_accrual_lines # TODO + has_many :superannuation_lines, :internal_name_singular => "super_line", :model_name => "SuperLine" + has_many :reimbursement_lines + + end + + end + end +end diff --git a/lib/xeroizer/models/payroll/settings.rb b/lib/xeroizer/models/payroll/settings.rb new file mode 100644 index 00000000..568d51cf --- /dev/null +++ b/lib/xeroizer/models/payroll/settings.rb @@ -0,0 +1,27 @@ +module Xeroizer + module Record + module Payroll + + class SettingModel < PayrollBaseModel + + set_permissions :read + + set_standalone_model true + set_xml_root_name 'Settings' + set_xml_node_name 'Settings' + end + + class Setting < PayrollBase + + set_primary_key false + + has_many :accounts + has_many :timesheet_categories, :model_name => 'TimesheetCategories' + has_many :employee_groups, :model_name => 'EmployeeGroups' + integer :days_in_payroll_year + + end + + end + end +end diff --git a/lib/xeroizer/models/payroll/super_line.rb b/lib/xeroizer/models/payroll/super_line.rb index 31035c41..2500de52 100644 --- a/lib/xeroizer/models/payroll/super_line.rb +++ b/lib/xeroizer/models/payroll/super_line.rb @@ -15,8 +15,11 @@ class SuperLine < PayrollBase string :expense_account_code string :liability_account_code + date :payment_date_for_this_period + decimal :minimum_monthly_earnings decimal :percentage + decimal :amount end diff --git a/lib/xeroizer/models/payroll/timesheet_categories.rb b/lib/xeroizer/models/payroll/timesheet_categories.rb new file mode 100644 index 00000000..fb1572ba --- /dev/null +++ b/lib/xeroizer/models/payroll/timesheet_categories.rb @@ -0,0 +1,24 @@ +module Xeroizer + module Record + module Payroll + + class TimesheetCategoriesModel < PayrollBaseModel + + set_standalone_model true + set_xml_root_name 'TimesheetCategories' + set_xml_node_name 'TimesheetCategories' + end + + # child of TrackingCategories + class TimesheetCategories < PayrollBase + + set_primary_key false + + guid :tracking_category_id + string :tracking_category_name + + end + + end + end +end diff --git a/lib/xeroizer/models/payroll/tracking_categories.rb b/lib/xeroizer/models/payroll/tracking_categories.rb new file mode 100644 index 00000000..ff3a632a --- /dev/null +++ b/lib/xeroizer/models/payroll/tracking_categories.rb @@ -0,0 +1,26 @@ +module Xeroizer + module Record + module Payroll + + class TrackingCategoriesModel < PayrollBaseModel + + set_permissions :read + + set_standalone_model true + set_xml_root_name 'TrackingCategories' + set_xml_node_name 'TrackingCategories' + end + + # child of Settings + class TrackingCategories < PayrollBase + + set_primary_key false + + has_many :employee_groups + has_many :timesheet_categories + + end + + end + end +end diff --git a/lib/xeroizer/payroll_application.rb b/lib/xeroizer/payroll_application.rb index 4e63acd6..08cde9e6 100644 --- a/lib/xeroizer/payroll_application.rb +++ b/lib/xeroizer/payroll_application.rb @@ -29,6 +29,9 @@ def self.record(record_type) record :PayItem record :PayrollCalendar record :LeaveApplication + record :PayRun + record :Payslip + record :Setting def initialize(application) @application = application diff --git a/lib/xeroizer/record/base_model.rb b/lib/xeroizer/record/base_model.rb index 10b9d732..8fa077b0 100644 --- a/lib/xeroizer/record/base_model.rb +++ b/lib/xeroizer/record/base_model.rb @@ -18,7 +18,7 @@ class InvalidPermissionError < StandardError class_inheritable_attributes :xml_root_name class_inheritable_attributes :optional_xml_root_name class_inheritable_attributes :xml_node_name - class_inheritable_attributes :all_children_are_subtypes + class_inheritable_attributes :standalone_model include BaseModelHttpProxy @@ -71,8 +71,8 @@ def set_optional_xml_root_name(optional_root_name) # If this is true, the tag isn't expected. So it would be # # Example: http://developer.xero.com/payroll-api/PayItems/#GET - def set_all_children_are_subtypes(boolean) - self.all_children_are_subtypes = boolean + def set_standalone_model(boolean) + self.standalone_model = boolean end end @@ -180,7 +180,7 @@ def parse_response(response_xml, options = {}) if model_name == response_model_name @response = response parse_records(response, elements, (options[:base_module] || Xeroizer::Record)) - elsif self.class.all_children_are_subtypes && self.class.xml_root_name == elements.first.parent.name + elsif self.class.standalone_model && self.class.xml_root_name == elements.first.parent.name @response = response parse_records(response, elements, (options[:base_module] || Xeroizer::Record), true) end @@ -190,21 +190,31 @@ def parse_response(response_xml, options = {}) protected # Parse the records part of the XML response and builds model instances as necessary. - def parse_records(response, elements, base_module, all_children_are_subtypes = false) + def parse_records(response, elements, base_module, standalone_model = false) elements.each do | element | - new_record = model_class.build_from_node(element, self, base_module, all_children_are_subtypes) + new_record = model_class.build_from_node(element, self, base_module, standalone_model) if element.attribute('status').try(:value) == 'ERROR' new_record.errors = [] element.xpath('.//ValidationError').each do |err| new_record.errors << err.text.gsub(/^\s+/, '').gsub(/\s+$/, '') end end - if all_children_are_subtypes + if standalone_model if response.response_items.count == 0 response.response_items << new_record else - field_to_fill = model_class.fields.find {|f| new_record[f[0]].count > 0} - (response.response_items.first[field_to_fill[0]] = new_record[field_to_fill[0]]) unless field_to_fill.nil? + # http://developer.xero.com/documentation/payroll-api/settings/ + # tracking categories have subcategories of timesheet categoires and employee groups + # which we group together here as it's much easier to model + fields_to_fill = model_class.fields.find_all do |f| + new_record_field = new_record[f[0]] + if new_record_field.respond_to?(:count) + new_record_field.count > 0 + else + !new_record_field.nil? + end + end + fields_to_fill.each {|field| response.response_items.first[field[0]] = new_record[field[0]]} end else response.response_items << new_record diff --git a/lib/xeroizer/record/record_association_helper.rb b/lib/xeroizer/record/record_association_helper.rb index 8f822155..5a2b9c12 100644 --- a/lib/xeroizer/record/record_association_helper.rb +++ b/lib/xeroizer/record/record_association_helper.rb @@ -16,6 +16,7 @@ def belongs_to(field_name, options = {}) # Create a #build_record_name method to build the record. define_method "build_#{internal_singular_field_name}" do | *args | + attributes = args.size == 1 ? args.first : {} # The name of the record model. diff --git a/lib/xeroizer/record/xml_helper.rb b/lib/xeroizer/record/xml_helper.rb index 2d0246d2..465c4eba 100644 --- a/lib/xeroizer/record/xml_helper.rb +++ b/lib/xeroizer/record/xml_helper.rb @@ -12,10 +12,10 @@ def self.included(base) module ClassMethods # Build a record instance from the XML node. - def build_from_node(node, parent, base_module, all_children_are_subtypes = false) + def build_from_node(node, parent, base_module, standalone_model = false) record = new(parent) node.elements.each do | element | - element_name = all_children_are_subtypes ? element.name.to_s.pluralize : element.name.to_s + element_name = standalone_model ? element.name.to_s.pluralize : element.name.to_s field = self.fields[element_name.underscore.to_sym] if field value = case field[:type] @@ -33,9 +33,9 @@ def build_from_node(node, parent, base_module, all_children_are_subtypes = false when :has_many if element.element_children.size > 0 - sub_field_name = field[:model_name] ? field[:model_name].to_sym : (all_children_are_subtypes ? element.name : element.children.first.name).to_sym + sub_field_name = field[:model_name] ? field[:model_name].to_sym : (standalone_model ? element.name : element.children.first.name).to_sym sub_parent = record.new_model_class(sub_field_name) - if all_children_are_subtypes + if standalone_model base_module.const_get(sub_field_name).build_from_node(element, sub_parent, base_module) else element.children.inject([]) do | list, element | @@ -56,7 +56,7 @@ def build_from_node(node, parent, base_module, all_children_are_subtypes = false end if field[:calculated] record.attributes[field[:internal_name]] = value - elsif all_children_are_subtypes + elsif standalone_model record.send("add_#{field[:internal_name].to_s.singularize}".to_sym, value) else record.send("#{field[:internal_name]}=".to_sym, value) diff --git a/privatekey.pem b/privatekey.pem new file mode 100644 index 00000000..e11a8af1 --- /dev/null +++ b/privatekey.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQDsJmYfpxau21Z1/7khZsiB6ZKX/BeurXUtX4LAEUNDjZe7Ytv1 +Vi+iWTkig+QXDO0YRiKqX6rLpQWlJDjIt6rzjSa8FEr6KBC+U2PBEv98FjQ8ZfvL +ty9vCewIrjk0iCifzgv/K14pA+aoJRE8IRmnoVVkDiGNj2Ds/YGkImgjYQIDAQAB +AoGALDsI97bBDeExMYrDLxlkRsjr1yG1gdclUmlIQRc6pQV5PPTIRAxvgZX6mJdh +elvGcLx6M7UVdW0kQIknRZj5IKbR9HP8SbuUtaKeofDAK2UFtVbnYB9qkWeYR0+q +IjDI1l+m7gK+AEg6/WVHC6woE94GTgSK1tBgN7HjjjEZkA0CQQD9qzvWf/9Sy2zP +KLq/5iEVMNHX5PmiqMqA7sPTtaQ4ij55snnlDHMKf8QxHXSMDlhmkU7vimQiKnto +UEh6KALfAkEA7lHziAB1fIWXGqerbcVTeJ3tI4YlbDVfesBvi7aEN3JZ5Q8YP9/n +BXNeqRfRnAPdFfkzR5MGZ+osjJhcdPjhvwJAI2KdiEB2p2AFH6i41EgP2VrkCs/A +GvacuPuViZTPAawXJvbEljT0X0SPY6KOPXNK1ZPzhOqzKSjv6g847QFj1QJAcRtz ++Zg+Kls82+m38uE0PIq3gaSpHjI2nou2ZRi6p5YeFBiV6braajvXMWmcke9DfqpH +LDEbWTZK7m9hciKtAQJBALBcn/xLbuP7xnn8SVdqEfxgK8L2rVJvrSl+yepohLHG +QGOpLqvJUjG4mU8Go5YPwrkyDdKpqsbxKB7ax1OGLoc= +-----END RSA PRIVATE KEY----- diff --git a/public_privatekey.pfx b/public_privatekey.pfx new file mode 100644 index 0000000000000000000000000000000000000000..0b2a6168bf91ca2bec288759357ccef5d87a7383 GIT binary patch literal 1885 zcmY+EXH*ji7KKTt1Vo9n0Rag`YLFgU5JY+lEf~f~v4BVrf;jYKgc0doii9cE83s5I^5`!kLQH?!Ou9Qp_lN;OmE?Df*wL|$@iMe%;kg(Jo zAmKc^smMd-yv&%r#YR$d0OE45Mfg3sxHu46RLOZ{cH7E4+d$&nc$0h}y?MKA;7-Sq zG^lk)*}skD3yQKnJ+$m|#s3$Z`re|rmMHArQZMSogi=8?%TmxV9 zi}bR{TOdQ#tC;YRJDk^0yWP!ob0oU9J-o_XxCMQl?ZTc4@^>xOIQ!I#ie(>Yrj12} z8|Ys}!-~RO2CXQ0p6;Yu=b)mO1XZ5Wd+S2j?eVo|&O1nacQ6w?aq?O(I$%I5g0)a+Q4 zuGY7wV4d%KEbnzBS?61wYK_{+z6&gvc9G!v1D5x;ozvoqP7wXt-zq64`|j(W_&_x- zZ1JWSrN!Pjn6VaO@!BhUzAbVRnSu3qcD;`0T2WYW{UAkMl_a$$8VPL-H!&^$8bx#9 z6J)pJBBG5nv+e!=JXs=+xD^Te0!aZTW@tR9n|%S4fZd;b%7DjB#6CIc5w<=NAjgmU~R{_FtY zDJ(t}^Z&0EKtEdnP`=T2^lswr&sI1+4?o0!ULj$~puZ*0@DEJqBdcknov_5Cd83a# zE`k%$W zQFS>x+I|6yk+0oC3k~En4x?dqNUEIKx7@2L7OjSny1B`ruCz|%m^En}_ zyv5qDJ^9Q_W1W*KiK~}sNUv9u6DBuFK6Y$|>-eS%DHN-7!mFV|6U%!LskvvR-tgy( zE$GFqBZXy!M|sW3L5?Xh-vf91RsHX{`56M!@UoIW1lJ`;8=?2rKmv&+Gd5ssL^l46 zylnl*ZCU#E?lHR~RR5+NUUDD``P%-0WMS!%;X?sv=oe9$%<1@aA#V8^KXoZi4D=Jj zYpsmVQkCpvKqr2W65eQd0f4s68753k8&nXpNr7X;u6GIrCwxj0DDnTgn(e$2FBoB@ zc#AFt;%)-ADWa+E@062*@r5WQlo*N)3X$ak0R 'http://developer.xero.com/payroll-api/') + +xero.logger = Logger.new(STDOUT) + +puts xero.Employee.url +puts xero.Timesheet.url \ No newline at end of file diff --git a/test/stub_responses/payroll_leave_applications.xml b/test/stub_responses/payroll_leave_applications.xml index 77dbef09..88dda278 100644 --- a/test/stub_responses/payroll_leave_applications.xml +++ b/test/stub_responses/payroll_leave_applications.xml @@ -5,10 +5,10 @@ 2013-05-30T05:25:03.0599507Z - 07c60af4-5c86-44d9-8146-fab3733a087c + fb4ebd68-6568-41eb-96ab-628a0f54b4b8 ACTIVE - Alex - Ghiculescu + LeBron + James 2013-04-18T01:29:53 diff --git a/test/stub_responses/payroll_pay_runs.xml b/test/stub_responses/payroll_pay_runs.xml new file mode 100644 index 00000000..b26cd55d --- /dev/null +++ b/test/stub_responses/payroll_pay_runs.xml @@ -0,0 +1,34 @@ + + a1b8088e-4166-48e9-9e29-5b5919cac0a0 + OK + Xeroizer test + 2013-05-30T05:25:03.0599507Z + + + 260.04 + 18831.25 + e3bdb2f7-2b20-45e6-ac8d-ec67d17de9f4 + 2012-01-07T00:00:00 + 2012-01-01T00:00:00 + Posted + 2012-01-08T00:00:00 + bfac31bd-ea62-4fc8-a5e7-7965d9504b15 + 2539.97 + 6651.00 + 25742.29 + + + 260.04 + 22463.25 + 7c998e04-1cee-4a19-bfe6-3cbfd5cb9cea + 2012-01-14T00:00:00 + 2012-01-08T00:00:00 + Posted + 2012-01-15T00:00:00 + bfac31bd-ea62-4fc8-a5e7-7965d9504b15 + 2892.78 + 6939.00 + 29662.29 + + + \ No newline at end of file diff --git a/test/stub_responses/records/payroll_employee-07c60af4-5c86-44d9-8146-fab3733a087c.xml b/test/stub_responses/records/payroll_employee-07c60af4-5c86-44d9-8146-fab3733a087c.xml deleted file mode 100644 index 31a929c1..00000000 --- a/test/stub_responses/records/payroll_employee-07c60af4-5c86-44d9-8146-fab3733a087c.xml +++ /dev/null @@ -1,147 +0,0 @@ - - 07c60af4-5c86-44d9-8146-fab3733a087d - OK - Demo AU - 2011-05-31T01:18:05.1105867Z - - - 07c60af4-5c86-44d9-8146-fab3733a087c - James - Lebron - ACTIVE - JL@madeup.email.com - 1978-08-13T00:00:00 - M - - 123 Main St - St. Kilda - VIC - 3182 - AUSTRALIA - - 0400-000-123 - 408-230-9732 - 2012-01-30T00:00:00 - 72e962d1-fcac-4083-8a71-742bb3e7ae14 - cb8e4706-2fdc-4170-aebd-0ffb855557f5 - 2013-04-01T23:02:36 - false - - false - true - true - false - false - false - 2013-04-26T00:04:48 - - - - Salary - James Lebron Savings - 122344 - 345678 - false - 20.0000 - - - Salary - James Lebron - 123443 - 2345678 - true - - - - 2012-07-01T00:00:00 - - - 72e962d1-fcac-4083-8a71-742bb3e7ae14 - 13333.33 - - - - - 59cd9d04-4521-4cc3-93ac-7841651ff407 - 40.00 - - - 4000.00 - - - 4333d5cd-53a5-4c31-98e5-a8b4e5676b0b - SGC - 3999.99 - - - - - - 742998cb-7584-4ecf-aa88-d694f59c50f9 - 100.0000 - - - 3bc7b584-a49f-40c6-ac5e-891f382785c9 - 40.0000 - - - - - - - 72e962d1-fcac-4083-8a71-742bb3e7ae14 - ANNUALSALARY - 40000.00 - 38.0000 - - - - - 59cd9d04-4521-4cc3-93ac-7841651ff407 - FIXEDAMOUNT - 10.00 - - - - - 4333d5cd-53a5-4c31-98e5-a8b4e5676b0b - SGC - PERCENTAGEOFEARNINGS - 450.00 - 478 - 826 - 9.0000 - - - 4333d5cd-53a5-4c31-98e5-a8b4e5676b0b - SALARYSACRIFICE - FIXEDAMOUNT - 478 - 826 - 50.0000 - - - - - - 742998cb-7584-4ecf-aa88-d694f59c50f9 - FIXEDAMOUNTEACHPERIOD - 152.0000 - - - 3bc7b584-a49f-40c6-ac5e-891f382785c9 - FIXEDAMOUNTEACHPERIOD - 76.0000 - - - - - - 4333d5cd-53a5-4c31-98e5-a8b4e5676b0b - 2187a42b-639a-45cb-9eed-cd4ae488306a - 1234 - - - - - \ No newline at end of file diff --git a/test/stub_responses/records/payroll_employee-fb4ebd68-6568-41eb-96ab-628a0f54b4b8.xml b/test/stub_responses/records/payroll_employee-fb4ebd68-6568-41eb-96ab-628a0f54b4b8.xml index 08fa1ede..0ebd36ac 100644 --- a/test/stub_responses/records/payroll_employee-fb4ebd68-6568-41eb-96ab-628a0f54b4b8.xml +++ b/test/stub_responses/records/payroll_employee-fb4ebd68-6568-41eb-96ab-628a0f54b4b8.xml @@ -5,7 +5,7 @@ 2011-05-31T01:18:05.1105867Z - 07c60af4-5c86-44d9-8146-fab3733a087c + fb4ebd68-6568-41eb-96ab-628a0f54b4b8 James Lebron ACTIVE diff --git a/test/stub_responses/records/payroll_leave_application-07c60af4-5c86-44d9-8146-fab3733a087c.xml b/test/stub_responses/records/payroll_leave_application-fb4ebd68-6568-41eb-96ab-628a0f54b4b8.xml similarity index 94% rename from test/stub_responses/records/payroll_leave_application-07c60af4-5c86-44d9-8146-fab3733a087c.xml rename to test/stub_responses/records/payroll_leave_application-fb4ebd68-6568-41eb-96ab-628a0f54b4b8.xml index e2ca22e2..770adc67 100644 --- a/test/stub_responses/records/payroll_leave_application-07c60af4-5c86-44d9-8146-fab3733a087c.xml +++ b/test/stub_responses/records/payroll_leave_application-fb4ebd68-6568-41eb-96ab-628a0f54b4b8.xml @@ -6,7 +6,7 @@ e0eb6747-7c17-4075-b804-989f8d4e5d39 - 07c60af4-5c86-44d9-8146-fab3733a087c + fb4ebd68-6568-41eb-96ab-628a0f54b4b8 742998cb-7584-4ecf-aa88-d694f59c50f9 diff --git a/test/stub_responses/records/payroll_payroll_run-7c998e04-1cee-4a19-bfe6-3cbfd5cb9cea.xml b/test/stub_responses/records/payroll_payroll_run-7c998e04-1cee-4a19-bfe6-3cbfd5cb9cea.xml new file mode 100644 index 00000000..50d463e8 --- /dev/null +++ b/test/stub_responses/records/payroll_payroll_run-7c998e04-1cee-4a19-bfe6-3cbfd5cb9cea.xml @@ -0,0 +1,62 @@ + + 49713875-ad73-492c-b6ac-2d265a5fe862 + OK + Demo AU + 2011-05-31T01:18:05.1105867Z + + + 260.04 + 18831.25 + 7c998e04-1cee-4a19-bfe6-3cbfd5cb9cea + 2012-01-07T00:00:00 + 2012-01-01T00:00:00 + Posted + 2012-01-08T00:00:00 + bfac31bd-ea62-4fc8-a5e7-7965d9504b15 + + + 0.00 + f3f29c96-77d7-44f0-80fc-0e87dea2320d + Bucky + 2012-03-27T23:33:35 + Lasek + 441.00 + 2d3a0cf2-6b1c-499d-a2bb-dda0cdad7634 + 0.00 + 43.56 + 43.00 + 484.00 + + + 0.00 + dcdb554d-2dbf-4a4f-99dd-c3fc6fafb7f3 + Chet + 2012-03-27T23:33:39 + Taylor + 1261.20 + 3586f5b4-9a04-4c2d-8ab6-3e1b34de0b62 + 0.00 + 151.31 + 420.00 + 1681.20 + + + 0.00 + c3896c4a-da46-489c-89c6-e48bcef655ef + Ray + 2012-03-27T23:41:28 + Barbee + 889.33 + c7196baa-3248-4801-ad7b-ae353b7b2f8c + 0.00 + 100.11 + 223.00 + 1112.33 + + + 2539.97 + 6651.00 + 25742.29 + + + \ No newline at end of file diff --git a/test/stub_responses/records/payroll_payroll_run-e3bdb2f7-2b20-45e6-ac8d-ec67d17de9f4.xml b/test/stub_responses/records/payroll_payroll_run-e3bdb2f7-2b20-45e6-ac8d-ec67d17de9f4.xml new file mode 100644 index 00000000..47b7ca55 --- /dev/null +++ b/test/stub_responses/records/payroll_payroll_run-e3bdb2f7-2b20-45e6-ac8d-ec67d17de9f4.xml @@ -0,0 +1,62 @@ + + 49713875-ad73-492c-b6ac-2d265a5fe862 + OK + Demo AU + 2011-05-31T01:18:05.1105867Z + + + 260.04 + 18831.25 + e3bdb2f7-2b20-45e6-ac8d-ec67d17de9f4 + 2012-01-07T00:00:00 + 2012-01-01T00:00:00 + Posted + 2012-01-08T00:00:00 + bfac31bd-ea62-4fc8-a5e7-7965d9504b15 + + + 0.00 + f3f29c96-77d7-44f0-80fc-0e87dea2320d + Bucky + 2012-03-27T23:33:35 + Lasek + 441.00 + 2d3a0cf2-6b1c-499d-a2bb-dda0cdad7634 + 0.00 + 43.56 + 43.00 + 484.00 + + + 0.00 + dcdb554d-2dbf-4a4f-99dd-c3fc6fafb7f3 + Chet + 2012-03-27T23:33:39 + Taylor + 1261.20 + 3586f5b4-9a04-4c2d-8ab6-3e1b34de0b62 + 0.00 + 151.31 + 420.00 + 1681.20 + + + 0.00 + c3896c4a-da46-489c-89c6-e48bcef655ef + Ray + 2012-03-27T23:41:28 + Barbee + 889.33 + c7196baa-3248-4801-ad7b-ae353b7b2f8c + 0.00 + 100.11 + 223.00 + 1112.33 + + + 2539.97 + 6651.00 + 25742.29 + + + \ No newline at end of file diff --git a/test/test_helper.rb b/test/test_helper.rb index 4d3d63bf..42f52522 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -30,7 +30,12 @@ module TestHelper GUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/ unless defined?(GUID_REGEX) def get_file_as_string(filename) - File.read(File.dirname(__FILE__) + "/stub_responses/" + filename) + if File.exist?(File.dirname(__FILE__) + "/stub_responses/" + filename) + File.read(File.dirname(__FILE__) + "/stub_responses/" + filename) + else + puts "WARNING: File does not exist: #{filename}" + nil + end end def get_record_xml(type, id = nil) @@ -52,6 +57,22 @@ def mock_api(model_name) client_for_stubbing.stubs(:http_get).with {|client, url, params| url =~ /#{model_name}\/#{record.id}$/ }.returns(get_record_xml("#{model_name_for_file(model_name).underscore.singularize}".to_sym, record.id)) end end + + # some models have a parent-child relationship, where you should call: + # Child.find(parent.id) to find items of type child belonging to the parent + # eg. http://developer.xero.com/documentation/payroll-api/leaveapplications/ + def mock_child_relationship_api(child, parent) + mock_api(child) + mock_api(parent) + # grab the ID of each parent record + # if we call api/child/parent_id, return the appropriate child xml + @client.send("#{parent.singularize}".to_sym).all.each do | record | + next if record.id.nil? + client_for_stubbing.stubs(:http_get).with {|client, url, params| + url =~ /#{child}\/#{record.id}$/ + }.returns(get_record_xml("#{model_name_for_file(child).underscore.singularize}".to_sym, record.id)) + end + end def mock_report_api(report_type) client_for_stubbing.stubs(:http_get).with { | client, url, params | url =~ /Reports\/#{report_type}$/ }.returns(get_report_xml(report_type)) diff --git a/test/unit/models/payroll/leave_application_test.rb b/test/unit/models/payroll/leave_application_test.rb index b83d5a58..561db9ab 100644 --- a/test/unit/models/payroll/leave_application_test.rb +++ b/test/unit/models/payroll/leave_application_test.rb @@ -5,18 +5,14 @@ class LeaveApplicationTest < Test::Unit::TestCase def setup @client = Xeroizer::PublicApplication.new(CONSUMER_KEY, CONSUMER_SECRET).payroll - #mock_api('Employees') - #mock_api('LeaveApplications') + mock_child_relationship_api('LeaveApplications', 'Employees') end -=begin test "can get leave applications using employee ID" do application = @client.LeaveApplication.find(@client.Employee.first.id) assert_not_nil application assert_equal 1, application.leave_periods.size - application.add_leave_periods(number_of_units: 48.00, pay_period_end_date: Date.today) - application.save + application.add_leave_period(number_of_units: 48.00, pay_period_end_date: Date.today) assert_equal 2, application.leave_periods.size end -=end end diff --git a/test/unit/models/payroll/pay_runs_test.rb b/test/unit/models/payroll/pay_runs_test.rb new file mode 100644 index 00000000..e04a614a --- /dev/null +++ b/test/unit/models/payroll/pay_runs_test.rb @@ -0,0 +1,15 @@ +require 'test_helper' + +class PayRunsTest < Test::Unit::TestCase + include TestHelper + + def setup + @client = Xeroizer::PublicApplication.new(CONSUMER_KEY, CONSUMER_SECRET).payroll + mock_api('PayRuns') + end + + test "get all" do + runs = @client.PayRun.all + assert_equal 2, runs.length + end +end From 8ad0b837765d6b85c1670c736d7d376b72189ad1 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Sat, 30 Nov 2013 15:05:47 +1000 Subject: [PATCH 17/82] ActiveSupport::Memoizable is no longer used, per https://github.com/waynerobinson/xeroizer/pull/82 --- lib/xeroizer.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/xeroizer.rb b/lib/xeroizer.rb index a8dcdeb7..e7f7a3cb 100644 --- a/lib/xeroizer.rb +++ b/lib/xeroizer.rb @@ -2,7 +2,6 @@ require 'date' require 'forwardable' require 'active_support/inflector' -require 'active_support/memoizable' # require "active_support/core_ext" require 'oauth' require 'oauth/signature/rsa/sha1' From 88bee8b191fd3fd4900d1c164978e5359f8628c5 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Thu, 19 Dec 2013 15:13:09 +1000 Subject: [PATCH 18/82] add tax declaration --- lib/xeroizer.rb | 3 ++- lib/xeroizer/models/payroll/employee.rb | 1 + .../models/payroll/tax_declaration.rb | 27 +++++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 lib/xeroizer/models/payroll/tax_declaration.rb diff --git a/lib/xeroizer.rb b/lib/xeroizer.rb index 5175dbea..91da443f 100644 --- a/lib/xeroizer.rb +++ b/lib/xeroizer.rb @@ -43,7 +43,8 @@ 'leave_application', 'leave_period', 'pay_items', 'deduction_type', 'earnings_rate', 'reimbursement_type', 'leave_type', 'payroll_calendar', 'pay_template', 'super_membership', 'leave_line', 'reimbursement_line', 'super_line', 'deduction_line', 'earnings_line', 'opening_balance', - 'pay_run', 'settings', 'tracking_categories', 'employee_groups', 'timesheet_categories', 'account'].each do |payroll_model| + 'pay_run', 'settings', 'tracking_categories', 'employee_groups', 'timesheet_categories', 'account', + 'tax_declaration'].each do |payroll_model| require "xeroizer/models/payroll/#{payroll_model}" end diff --git a/lib/xeroizer/models/payroll/employee.rb b/lib/xeroizer/models/payroll/employee.rb index f965c4e9..b6692974 100644 --- a/lib/xeroizer/models/payroll/employee.rb +++ b/lib/xeroizer/models/payroll/employee.rb @@ -36,6 +36,7 @@ class Employee < PayrollBase datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC' belongs_to :home_address, :internal_name_singular => "home_address", :model_name => "HomeAddress" + belongs_to :tax_declaration, :internal_name_singular => "tax_declaration", :model_name => "TaxDeclaration" has_many :bank_accounts belongs_to :pay_template, :internal_name_singular => "pay_template", :model_name => "PayTemplate" belongs_to :opening_balances, :internal_name_singular => "opening_balance", :model_name => "OpeningBalances" diff --git a/lib/xeroizer/models/payroll/tax_declaration.rb b/lib/xeroizer/models/payroll/tax_declaration.rb new file mode 100644 index 00000000..09ddb64d --- /dev/null +++ b/lib/xeroizer/models/payroll/tax_declaration.rb @@ -0,0 +1,27 @@ +module Xeroizer + module Record + module Payroll + + class TaxDeclarationModel < PayrollBaseModel + + end + + class TaxDeclaration < PayrollBase + + guid :employee_id + string :employment_basis + string :tfn_exemption_type, :api_name => 'TFNExemptionType' + boolean :australian_resident_for_tax_purposes + boolean :tax_free_threshold_claimed + decimal :tax_offset_estimated_amount + boolean :has_help_debt, :api_name => 'HasHELPDebt' + boolean :has_sfss_debt, :api_name => 'HasSFSSDebt' + decimal :upward_variation_tax_withholding_amount + boolean :eligible_to_receive_leave_loading + decimal :approved_withholding_variation_percentage + + end + + end + end +end From c0c509cb09497eb14640848ab72e540aa989d8a8 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Thu, 20 Mar 2014 13:50:52 +1000 Subject: [PATCH 19/82] always include an xml accept tag --- lib/xeroizer/http.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/xeroizer/http.rb b/lib/xeroizer/http.rb index 6a0a6695..9815d7b5 100644 --- a/lib/xeroizer/http.rb +++ b/lib/xeroizer/http.rb @@ -74,6 +74,8 @@ def http_request(client, method, url, body, params = {}) when Symbol then ACCEPT_MIME_MAP[response_type] else response_type end + else + headers['Accept'] = "application/xml" end if params.any? From 99f8e714a7d7d81f09de606b6ce21920fbfc25fb Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Tue, 13 May 2014 17:18:31 +1000 Subject: [PATCH 20/82] add payslip endpoint --- lib/xeroizer.rb | 2 +- lib/xeroizer/models/payroll/earnings_line.rb | 3 ++- .../models/payroll/leave_accrual_line.rb | 21 +++++++++++++++ lib/xeroizer/models/payroll/pay_run.rb | 2 +- lib/xeroizer/models/payroll/payslip.rb | 26 ++++++++++++++----- lib/xeroizer/models/payroll/tax_line.rb | 24 +++++++++++++++++ .../models/payroll/timesheet_earnings_line.rb | 20 ++++++++++++++ lib/xeroizer/record/base_model.rb | 18 ++++++++++++- lib/xeroizer/record/xml_helper.rb | 4 +++ 9 files changed, 110 insertions(+), 10 deletions(-) create mode 100644 lib/xeroizer/models/payroll/leave_accrual_line.rb create mode 100644 lib/xeroizer/models/payroll/tax_line.rb create mode 100644 lib/xeroizer/models/payroll/timesheet_earnings_line.rb diff --git a/lib/xeroizer.rb b/lib/xeroizer.rb index 91da443f..c59a088e 100644 --- a/lib/xeroizer.rb +++ b/lib/xeroizer.rb @@ -44,7 +44,7 @@ 'reimbursement_type', 'leave_type', 'payroll_calendar', 'pay_template', 'super_membership', 'leave_line', 'reimbursement_line', 'super_line', 'deduction_line', 'earnings_line', 'opening_balance', 'pay_run', 'settings', 'tracking_categories', 'employee_groups', 'timesheet_categories', 'account', - 'tax_declaration'].each do |payroll_model| + 'tax_declaration', 'payslip', 'timesheet_earnings_line', 'tax_line', 'leave_accrual_line'].each do |payroll_model| require "xeroizer/models/payroll/#{payroll_model}" end diff --git a/lib/xeroizer/models/payroll/earnings_line.rb b/lib/xeroizer/models/payroll/earnings_line.rb index 9d54cc64..337cac2e 100644 --- a/lib/xeroizer/models/payroll/earnings_line.rb +++ b/lib/xeroizer/models/payroll/earnings_line.rb @@ -6,13 +6,14 @@ class EarningsLineModel < PayrollBaseModel end - # child of PayTemplate + # child of PayTemplate, Payslip class EarningsLine < PayrollBase guid :earnings_rate_id string :calculation_type # http://developer.xero.com/payroll-api/types-and-codes/#EarningsRateCalculationType decimal :number_of_units_per_week + decimal :number_of_units decimal :annual_salary decimal :rate_per_unit decimal :normal_number_of_units diff --git a/lib/xeroizer/models/payroll/leave_accrual_line.rb b/lib/xeroizer/models/payroll/leave_accrual_line.rb new file mode 100644 index 00000000..29a4e090 --- /dev/null +++ b/lib/xeroizer/models/payroll/leave_accrual_line.rb @@ -0,0 +1,21 @@ +module Xeroizer + module Record + module Payroll + + class LeaveAccrualLineModel < PayrollBaseModel + + end + + # http://developer.xero.com/documentation/payroll-api/payslip/#LeaveAccrualLine + class LeaveAccrualLine < PayrollBase + decimal :number_of_units + + boolean :auto_calculate + + guid :leave_type_id + + end + + end + end +end diff --git a/lib/xeroizer/models/payroll/pay_run.rb b/lib/xeroizer/models/payroll/pay_run.rb index 2953346c..aa74bf84 100644 --- a/lib/xeroizer/models/payroll/pay_run.rb +++ b/lib/xeroizer/models/payroll/pay_run.rb @@ -31,7 +31,7 @@ class PayRun < PayrollBase string :pay_run_status string :payslip_message - #has_many :payslips + has_many :payslips end diff --git a/lib/xeroizer/models/payroll/payslip.rb b/lib/xeroizer/models/payroll/payslip.rb index 155c864e..f38b490c 100644 --- a/lib/xeroizer/models/payroll/payslip.rb +++ b/lib/xeroizer/models/payroll/payslip.rb @@ -4,6 +4,10 @@ module Payroll class PayslipModel < PayrollBaseModel + set_api_controller_name 'Payslip' + + set_api_response_padding 'Payslip' + set_permissions :read, :update end @@ -14,15 +18,25 @@ class Payslip < PayrollBase set_primary_key :payslip_id guid :payslip_id - guid :employee_id - has_many :earnings_lines - #has_many :timesheet_earnings_lines # TODO - has_many :deduction_lines - #has_many :leave_accrual_lines # TODO + string :first_name + string :last_name + + decimal :wages + decimal :deductions + decimal :net_pay + decimal :tax + decimal :super + decimal :reimubrsements + + has_many :earnings_lines, model_name: "EarningsLine" + has_many :tax_lines, model_name: "TaxLine" + has_many :timesheet_earnings_lines, model_name: "TimesheetEarningsLine" + has_many :deduction_lines, model_name: "DeductionLine" + has_many :leave_accrual_lines, model_name: "LeaveAccrualLine" has_many :superannuation_lines, :internal_name_singular => "super_line", :model_name => "SuperLine" - has_many :reimbursement_lines + has_many :reimbursement_lines, model_name: "ReimbursementLine" end diff --git a/lib/xeroizer/models/payroll/tax_line.rb b/lib/xeroizer/models/payroll/tax_line.rb new file mode 100644 index 00000000..e9606568 --- /dev/null +++ b/lib/xeroizer/models/payroll/tax_line.rb @@ -0,0 +1,24 @@ +module Xeroizer + module Record + module Payroll + + class TaxLineModel < PayrollBaseModel + + end + + # http://developer.xero.com/documentation/payroll-api/payslip/#TaxLine + class TaxLine < PayrollBase + + string :tax_type_name + string :description + string :liability_account + + decimal :amount + + guid :payslilp_tax_line_id + + end + + end + end +end diff --git a/lib/xeroizer/models/payroll/timesheet_earnings_line.rb b/lib/xeroizer/models/payroll/timesheet_earnings_line.rb new file mode 100644 index 00000000..2ae6d58e --- /dev/null +++ b/lib/xeroizer/models/payroll/timesheet_earnings_line.rb @@ -0,0 +1,20 @@ +module Xeroizer + module Record + module Payroll + + class TimesheetEarningsLineModel < PayrollBaseModel + + end + + # child of Payslip + class TimesheetEarningsLine < PayrollBase + + guid :earnings_rate_id + decimal :amount + decimal :rate_per_unit + + end + + end + end +end diff --git a/lib/xeroizer/record/base_model.rb b/lib/xeroizer/record/base_model.rb index 731ee591..ad292e34 100644 --- a/lib/xeroizer/record/base_model.rb +++ b/lib/xeroizer/record/base_model.rb @@ -19,6 +19,8 @@ class InvalidPermissionError < StandardError class_inheritable_attributes :optional_xml_root_name class_inheritable_attributes :xml_node_name class_inheritable_attributes :standalone_model + class_inheritable_attributes :before_padding + class_inheritable_attributes :after_padding DEFAULT_RECORDS_PER_BATCH_SAVE = 2000 @@ -76,6 +78,17 @@ def set_optional_xml_root_name(optional_root_name) def set_standalone_model(boolean) self.standalone_model = boolean end + + # Usually the xml structure will be + # Provide wrapping if the response is + def set_api_response_padding(padding) + self.before_padding = "<#{padding.pluralize}><#{padding}>" + self.after_padding = "" + end + + def pad_api_response? + self.before_padding && self.after_padding + end end @@ -183,7 +196,10 @@ def batch_save(chunk_size = DEFAULT_RECORDS_PER_BATCH_SAVE) def parse_response(response_xml, options = {}) Response.parse(response_xml, options) do | response, elements, response_model_name | - if model_name == response_model_name + if self.class.pad_api_response? + @response = response + parse_records(response, Nokogiri::XML("#{self.class.before_padding}#{elements.to_xml}#{self.class.after_padding}").root.elements, (options[:base_module] || Xeroizer::Record)) + elsif model_name == response_model_name @response = response parse_records(response, elements, (options[:base_module] || Xeroizer::Record)) elsif self.class.standalone_model && self.class.xml_root_name == elements.first.parent.name diff --git a/lib/xeroizer/record/xml_helper.rb b/lib/xeroizer/record/xml_helper.rb index 5d62a03e..e92758ca 100644 --- a/lib/xeroizer/record/xml_helper.rb +++ b/lib/xeroizer/record/xml_helper.rb @@ -42,6 +42,8 @@ def build_from_node(node, parent, base_module, standalone_model = false) list << base_module.const_get(sub_field_name).build_from_node(element, sub_parent, base_module) end end + else + [] end when :has_array @@ -51,6 +53,8 @@ def build_from_node(node, parent, base_module, standalone_model = false) element.element_children.inject([]) do |list, child| list << base_module.const_get(sub_field_name).build_from_node(child, sub_parent, base_module) end + else + [] end end From 956fe65df109b1f47dcbe678e55ea4b8a2108d64 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Fri, 16 May 2014 16:15:57 +1000 Subject: [PATCH 21/82] missing fields --- lib/xeroizer/models/payroll/payslip.rb | 1 + lib/xeroizer/models/payroll/timesheet_earnings_line.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/xeroizer/models/payroll/payslip.rb b/lib/xeroizer/models/payroll/payslip.rb index f38b490c..506e5525 100644 --- a/lib/xeroizer/models/payroll/payslip.rb +++ b/lib/xeroizer/models/payroll/payslip.rb @@ -19,6 +19,7 @@ class Payslip < PayrollBase guid :payslip_id guid :employee_id + guid :pay_run_id string :first_name string :last_name diff --git a/lib/xeroizer/models/payroll/timesheet_earnings_line.rb b/lib/xeroizer/models/payroll/timesheet_earnings_line.rb index 2ae6d58e..594fb690 100644 --- a/lib/xeroizer/models/payroll/timesheet_earnings_line.rb +++ b/lib/xeroizer/models/payroll/timesheet_earnings_line.rb @@ -10,7 +10,7 @@ class TimesheetEarningsLineModel < PayrollBaseModel class TimesheetEarningsLine < PayrollBase guid :earnings_rate_id - decimal :amount + decimal :number_of_units decimal :rate_per_unit end From fed9369a90e8610822ce02df3c5283aaa9a55d33 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Mon, 7 Jul 2014 12:28:01 +1000 Subject: [PATCH 22/82] add payslip superannuation lines, and don't load in empty text nodes when GETting --- lib/xeroizer.rb | 2 +- lib/xeroizer/models/payroll/payslip.rb | 2 +- .../models/payroll/superannuation_line.rb | 28 +++++++++++++++++++ lib/xeroizer/record/xml_helper.rb | 7 ++++- 4 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 lib/xeroizer/models/payroll/superannuation_line.rb diff --git a/lib/xeroizer.rb b/lib/xeroizer.rb index c59a088e..92862a09 100644 --- a/lib/xeroizer.rb +++ b/lib/xeroizer.rb @@ -44,7 +44,7 @@ 'reimbursement_type', 'leave_type', 'payroll_calendar', 'pay_template', 'super_membership', 'leave_line', 'reimbursement_line', 'super_line', 'deduction_line', 'earnings_line', 'opening_balance', 'pay_run', 'settings', 'tracking_categories', 'employee_groups', 'timesheet_categories', 'account', - 'tax_declaration', 'payslip', 'timesheet_earnings_line', 'tax_line', 'leave_accrual_line'].each do |payroll_model| + 'tax_declaration', 'payslip', 'timesheet_earnings_line', 'tax_line', 'leave_accrual_line', 'superannuation_line'].each do |payroll_model| require "xeroizer/models/payroll/#{payroll_model}" end diff --git a/lib/xeroizer/models/payroll/payslip.rb b/lib/xeroizer/models/payroll/payslip.rb index 506e5525..6774a4ca 100644 --- a/lib/xeroizer/models/payroll/payslip.rb +++ b/lib/xeroizer/models/payroll/payslip.rb @@ -36,7 +36,7 @@ class Payslip < PayrollBase has_many :timesheet_earnings_lines, model_name: "TimesheetEarningsLine" has_many :deduction_lines, model_name: "DeductionLine" has_many :leave_accrual_lines, model_name: "LeaveAccrualLine" - has_many :superannuation_lines, :internal_name_singular => "super_line", :model_name => "SuperLine" + has_many :superannuation_lines, :model_name => "SuperannuationLine" has_many :reimbursement_lines, model_name: "ReimbursementLine" end diff --git a/lib/xeroizer/models/payroll/superannuation_line.rb b/lib/xeroizer/models/payroll/superannuation_line.rb new file mode 100644 index 00000000..af826603 --- /dev/null +++ b/lib/xeroizer/models/payroll/superannuation_line.rb @@ -0,0 +1,28 @@ +module Xeroizer + module Record + module Payroll + + class SuperannuationLineModel < PayrollBaseModel + + end + + # child of Payslip. Same as SuperLine. + class SuperannuationLine < PayrollBase + + guid :super_membership_id + string :contribution_type # http://developer.xero.com/payroll-api/types-and-codes#SuperannuationContributionType + string :calculation_type # http://developer.xero.com/payroll-api/types-and-codes#SuperannuationCalculationType + string :expense_account_code + string :liability_account_code + + date :payment_date_for_this_period + + decimal :minimum_monthly_earnings + decimal :percentage + decimal :amount + + end + + end + end +end diff --git a/lib/xeroizer/record/xml_helper.rb b/lib/xeroizer/record/xml_helper.rb index e92758ca..6d3f4a3a 100644 --- a/lib/xeroizer/record/xml_helper.rb +++ b/lib/xeroizer/record/xml_helper.rb @@ -38,7 +38,7 @@ def build_from_node(node, parent, base_module, standalone_model = false) if standalone_model base_module.const_get(sub_field_name).build_from_node(element, sub_parent, base_module) else - element.children.inject([]) do | list, element | + remove_empty_text_nodes(element.children).inject([]) do | list, element | list << base_module.const_get(sub_field_name).build_from_node(element, sub_parent, base_module) end end @@ -71,6 +71,11 @@ def build_from_node(node, parent, base_module, standalone_model = false) parent.mark_clean(record) record end + + private + def remove_empty_text_nodes(children) + children.find_all {|c| !c.respond_to?(:text) || !c.text.strip.empty?} + end end From 60c0d8720e713dfb88b5173b103fe42978eb59a1 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Mon, 14 Jul 2014 12:22:17 +1000 Subject: [PATCH 23/82] fix home address param names --- lib/xeroizer/models/payroll/home_address.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/xeroizer/models/payroll/home_address.rb b/lib/xeroizer/models/payroll/home_address.rb index eae217f4..9d5de35c 100644 --- a/lib/xeroizer/models/payroll/home_address.rb +++ b/lib/xeroizer/models/payroll/home_address.rb @@ -8,10 +8,10 @@ class HomeAddressModel < PayrollBaseModel class HomeAddress < PayrollBase - string :address_line_1 - string :address_line_2 - string :address_line_3 - string :address_line_4 + string :address_line1 + string :address_line2 + string :address_line3 + string :address_line4 string :city string :region string :postal_code From a3db0a3e2d29273ff4fe128e015b0b3d496323bc Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Mon, 11 Aug 2014 14:25:09 +1000 Subject: [PATCH 24/82] add user model --- lib/xeroizer.rb | 2 +- lib/xeroizer/generic_application.rb | 1 + lib/xeroizer/models/user.rb | 23 +++++++++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 lib/xeroizer/models/user.rb diff --git a/lib/xeroizer.rb b/lib/xeroizer.rb index 92862a09..91515102 100644 --- a/lib/xeroizer.rb +++ b/lib/xeroizer.rb @@ -34,7 +34,7 @@ 'credit_note','currency','employee','invoice','item','item_purchase_details','item_sales_details', 'journal','journal_line','line_item','manual_journal','manual_journal_line','option','organisation', 'payment','phone','tax_rate','tracking_category','tracking_category_child', - 'journal_line_tracking_category'].each do |model| + 'journal_line_tracking_category', 'user'].each do |model| require "xeroizer/models/#{model}" end diff --git a/lib/xeroizer/generic_application.rb b/lib/xeroizer/generic_application.rb index 31704bad..422f5dc2 100644 --- a/lib/xeroizer/generic_application.rb +++ b/lib/xeroizer/generic_application.rb @@ -25,6 +25,7 @@ class GenericApplication record :Journal record :ManualJournal record :Organisation + record :User record :Payment record :TaxRate record :TrackingCategory diff --git a/lib/xeroizer/models/user.rb b/lib/xeroizer/models/user.rb new file mode 100644 index 00000000..36d154ec --- /dev/null +++ b/lib/xeroizer/models/user.rb @@ -0,0 +1,23 @@ +module Xeroizer + module Record + + class UserModel < BaseModel + + set_api_controller_name 'User' + set_permissions :read + + end + + class User < Base + + string :user_id, :api_name => 'UserID' + string :email_address + string :first_name + string :last_name + datetime :updated_date_utc, :api_name => 'UpdatedDateUTC' + boolean :is_subscriber + string :organisation_role + end + + end +end \ No newline at end of file From 4026f5c4c6a23edc4544c9a8ef7058b60fc270ef Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Mon, 8 Sep 2014 09:44:49 +1000 Subject: [PATCH 25/82] when adding a child to a record's list, should mark as dirty --- lib/xeroizer/record/record_association_helper.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/xeroizer/record/record_association_helper.rb b/lib/xeroizer/record/record_association_helper.rb index 5a2b9c12..6dc5ceb9 100644 --- a/lib/xeroizer/record/record_association_helper.rb +++ b/lib/xeroizer/record/record_association_helper.rb @@ -28,6 +28,7 @@ def belongs_to(field_name, options = {}) # Create a new record, binding it to it's parent instance. record = (options[:base_module] || Xeroizer::Record).const_get(model_name).build(attributes, model_parent) self.attributes[field_name] = record + self.parent.mark_dirty(self) if self.parent end end @@ -69,6 +70,7 @@ def has_array(field_name, options = {}) raise StandardError.new("Record #{record.class.name} is not a #{record_class.name}.") unless record.is_a?(record_class) self.attributes[field_name] ||= [] self.attributes[field_name] << record + self.parent.mark_dirty(self) if self.parent last_record = record end @@ -117,6 +119,7 @@ def has_many(field_name, options = {}) raise StandardError.new("Record #{record.class.name} is not a #{record_class.name}.") unless record.is_a?(record_class) self.attributes[field_name] ||= [] self.attributes[field_name] << record + self.parent.mark_dirty(self) if self.parent last_record = record end From dc93f4adc3898f1adecfe3f06a0bf7e77fe7475e Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Thu, 11 Sep 2014 13:34:41 +1000 Subject: [PATCH 26/82] FIX; pay lines have a leave type ID, not a leave line ID --- lib/xeroizer/models/payroll/leave_line.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/xeroizer/models/payroll/leave_line.rb b/lib/xeroizer/models/payroll/leave_line.rb index 54149597..6ff13fc3 100644 --- a/lib/xeroizer/models/payroll/leave_line.rb +++ b/lib/xeroizer/models/payroll/leave_line.rb @@ -9,7 +9,7 @@ class LeaveLineModel < PayrollBaseModel # child of PayTemplate class LeaveLine < PayrollBase - guid :leave_line_id + guid :leave_type_id string :calculation_type # http://developer.xero.com/payroll-api/types-and-codes#LeaveTypeCalculationType decimal :annual_number_of_units From 9688bd8e261583816eb81ab3a8252bccf3fad68f Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Tue, 16 Sep 2014 09:46:02 +1000 Subject: [PATCH 27/82] FIX: missing API name for BSB field --- lib/xeroizer/models/payroll/bank_account.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/xeroizer/models/payroll/bank_account.rb b/lib/xeroizer/models/payroll/bank_account.rb index 71d5ce96..f160b118 100644 --- a/lib/xeroizer/models/payroll/bank_account.rb +++ b/lib/xeroizer/models/payroll/bank_account.rb @@ -10,7 +10,7 @@ class BankAccount < PayrollBase string :statement_text string :account_name - string :bsb + string :bsb, :api_name => 'BSB' string :account_number boolean :remainder decimal :percentage From 7ced464d8952484b7f8d3b3497290f64bb77fbea Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Fri, 17 Oct 2014 11:35:41 +1000 Subject: [PATCH 28/82] NEW: handle "no permission to access record" error --- lib/xeroizer/exceptions.rb | 2 ++ lib/xeroizer/http.rb | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/lib/xeroizer/exceptions.rb b/lib/xeroizer/exceptions.rb index 16a2c867..6a9c016c 100644 --- a/lib/xeroizer/exceptions.rb +++ b/lib/xeroizer/exceptions.rb @@ -58,6 +58,8 @@ def message class InvoiceNotFoundError < StandardError; end class CreditNoteNotFoundError < StandardError; end + + class LackingPermissionToAccessRecord < StandardError; end class MethodNotAllowed < StandardError diff --git a/lib/xeroizer/http.rb b/lib/xeroizer/http.rb index 9815d7b5..05c22b18 100644 --- a/lib/xeroizer/http.rb +++ b/lib/xeroizer/http.rb @@ -134,6 +134,10 @@ def http_request(client, method, url, body, params = {}) end def handle_oauth_error!(response) + if response.plain_body == "You do not have permission to access this resource." + raise LackingPermissionToAccessRecord.new(response) + end + error_details = CGI.parse(response.plain_body) description = error_details["oauth_problem_advice"].first From 8b68935d4e9bc7d0fe427d205ff97a8fadaeb049 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Fri, 17 Oct 2014 13:35:37 +1000 Subject: [PATCH 29/82] NEW: earnings line can have an amount, as a child of opening balance --- lib/xeroizer/models/payroll/earnings_line.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/xeroizer/models/payroll/earnings_line.rb b/lib/xeroizer/models/payroll/earnings_line.rb index 337cac2e..a2b542f8 100644 --- a/lib/xeroizer/models/payroll/earnings_line.rb +++ b/lib/xeroizer/models/payroll/earnings_line.rb @@ -6,7 +6,7 @@ class EarningsLineModel < PayrollBaseModel end - # child of PayTemplate, Payslip + # child of PayTemplate, Payslip, OpeningBalance class EarningsLine < PayrollBase guid :earnings_rate_id @@ -17,6 +17,7 @@ class EarningsLine < PayrollBase decimal :annual_salary decimal :rate_per_unit decimal :normal_number_of_units + decimal :amount end From eab9e8c2c5dc16d9c1334232a5cd4ddb42fef6bd Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Wed, 10 Dec 2014 13:50:17 +1000 Subject: [PATCH 30/82] FIX: if a belongs_to is nil'd, don't include it in the XML when posting back. fixes the problem of trying to save an employee with a read only opening balance - you can nil it before saving as a work around. --- lib/xeroizer/record/xml_helper.rb | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/xeroizer/record/xml_helper.rb b/lib/xeroizer/record/xml_helper.rb index 6d3f4a3a..f54ba9b6 100644 --- a/lib/xeroizer/record/xml_helper.rb +++ b/lib/xeroizer/record/xml_helper.rb @@ -137,8 +137,15 @@ def xml_value_from_field(b, field, value) when :datetime then b.tag!(field[:api_name], value.utc.strftime("%Y-%m-%dT%H:%M:%S")) when :belongs_to - value.to_xml(b) - nil + value_is_present = (value.respond_to?(:blank?) && !value.blank?) || (!value.nil? && (!value.respond_to?(:length) || (value.respond_to?(:length) && value.length != 0))) + # you may need to set a belongs_to to nil, at which point it defaults back to an array + # but in that case we don't want to include it in the XML response + if value_is_present + value.to_xml(b) + nil + else + nil + end when :has_many if value.size > 0 From 368cdfc3068731604c19297af787a1c1cef0cf1e Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Thu, 29 Jan 2015 19:05:06 +1000 Subject: [PATCH 31/82] NEW: alternative response message for not authorised to access API --- lib/xeroizer/http.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/xeroizer/http.rb b/lib/xeroizer/http.rb index 05c22b18..b1682182 100644 --- a/lib/xeroizer/http.rb +++ b/lib/xeroizer/http.rb @@ -134,7 +134,7 @@ def http_request(client, method, url, body, params = {}) end def handle_oauth_error!(response) - if response.plain_body == "You do not have permission to access this resource." + if response.plain_body == "You do not have permission to access this resource." || response.plain_body.match(/API access not authorised/i) raise LackingPermissionToAccessRecord.new(response) end From 732561bb26804a6424d8eed48270673850a25d4c Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Tue, 3 Feb 2015 17:44:16 +1000 Subject: [PATCH 32/82] FIX: typo --- lib/xeroizer/models/payroll/tax_line.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/xeroizer/models/payroll/tax_line.rb b/lib/xeroizer/models/payroll/tax_line.rb index e9606568..c785ec1a 100644 --- a/lib/xeroizer/models/payroll/tax_line.rb +++ b/lib/xeroizer/models/payroll/tax_line.rb @@ -15,7 +15,7 @@ class TaxLine < PayrollBase decimal :amount - guid :payslilp_tax_line_id + guid :payslip_tax_line_id end From 1878529c049babfa3d7f5c435f1711a51beaa8a0 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Mon, 23 Mar 2015 11:12:56 +1000 Subject: [PATCH 33/82] NEW: xero us payroll scopes --- lib/xeroizer/scopes.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/xeroizer/scopes.rb b/lib/xeroizer/scopes.rb index 8d024a28..9de3e0d9 100644 --- a/lib/xeroizer/scopes.rb +++ b/lib/xeroizer/scopes.rb @@ -3,5 +3,13 @@ class Scopes def self.all_payroll ['Employees', 'LeaveApplications', 'PayItems', 'PayrollCalendars', 'PayRuns', 'Payslip', 'SuperFunds', 'Settings', 'Timesheets'].map {|s| "payroll.#{s.downcase}"}.join(',') end + + def self.au_payroll + all_payroll + end + + def self.us_payroll + ['Employees', 'PayItems', 'PaySchedules', 'PayRuns', 'Paystubs', 'Worklocations', 'Settings', 'Timesheets'].map {|s| "payroll.#{s.downcase}"}.join(',') + end end end \ No newline at end of file From d62d5f7d59ef5aa08601e3667f5b3e8507e7a847 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Mon, 4 May 2015 14:32:05 +1000 Subject: [PATCH 34/82] FIX: wasn't getting NumberOfUnit from a timesheeet properly --- lib/xeroizer/record/xml_helper.rb | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/xeroizer/record/xml_helper.rb b/lib/xeroizer/record/xml_helper.rb index f54ba9b6..a02247cf 100644 --- a/lib/xeroizer/record/xml_helper.rb +++ b/lib/xeroizer/record/xml_helper.rb @@ -68,6 +68,26 @@ def build_from_node(node, parent, base_module, standalone_model = false) end end + # special case for array models eg. NumberOfUnit(s) in http://developer.xero.com/documentation/payroll-api/timesheets/ + if node.elements.empty? && parent.is_a?(Xeroizer::Record::PayrollArrayBaseModel) + field = self.fields[:value] + if field + element = node.children.first + value = case field[:type] + when :guid then element.text + when :string then element.text + when :boolean then (element.text == 'true') + when :integer then element.text.to_i + when :decimal then BigDecimal.new(element.text) + when :date then Date.parse(element.text) + when :datetime then Time.parse(element.text) + when :datetime_utc then ActiveSupport::TimeZone['UTC'].parse(element.text).utc + end + + record.value = value + end + end + parent.mark_clean(record) record end From 51454d390de52f1689647f1384dbbf16e20e8975 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Wed, 11 Nov 2015 11:08:06 +1000 Subject: [PATCH 35/82] NEW - add support for http://developer.xero.com/documentation/payroll-api/leavebalances/ --- lib/xeroizer.rb | 3 ++- lib/xeroizer/models/payroll/employee.rb | 1 + lib/xeroizer/models/payroll/leave_balance.rb | 21 +++++++++++++++++++ ...e-fb4ebd68-6568-41eb-96ab-628a0f54b4b8.xml | 14 +++++++++++++ ...-7c998e04-1cee-4a19-bfe6-3cbfd5cb9cea.xml} | 0 ...-e3bdb2f7-2b20-45e6-ac8d-ec67d17de9f4.xml} | 0 test/test_helper.rb | 1 - test/unit/models/payroll/employees_test.rb | 7 +++++++ 8 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 lib/xeroizer/models/payroll/leave_balance.rb rename test/stub_responses/records/{payroll_payroll_run-7c998e04-1cee-4a19-bfe6-3cbfd5cb9cea.xml => payroll_pay_run-7c998e04-1cee-4a19-bfe6-3cbfd5cb9cea.xml} (100%) rename test/stub_responses/records/{payroll_payroll_run-e3bdb2f7-2b20-45e6-ac8d-ec67d17de9f4.xml => payroll_pay_run-e3bdb2f7-2b20-45e6-ac8d-ec67d17de9f4.xml} (100%) diff --git a/lib/xeroizer.rb b/lib/xeroizer.rb index 003ef11a..502b9342 100644 --- a/lib/xeroizer.rb +++ b/lib/xeroizer.rb @@ -44,7 +44,8 @@ 'reimbursement_type', 'leave_type', 'payroll_calendar', 'pay_template', 'super_membership', 'leave_line', 'reimbursement_line', 'super_line', 'deduction_line', 'earnings_line', 'opening_balance', 'pay_run', 'settings', 'tracking_categories', 'employee_groups', 'timesheet_categories', 'account', - 'tax_declaration', 'payslip', 'timesheet_earnings_line', 'tax_line', 'leave_accrual_line', 'superannuation_line'].each do |payroll_model| + 'tax_declaration', 'payslip', 'timesheet_earnings_line', 'tax_line', 'leave_accrual_line', 'superannuation_line', + 'leave_balance'].each do |payroll_model| require "xeroizer/models/payroll/#{payroll_model}" end diff --git a/lib/xeroizer/models/payroll/employee.rb b/lib/xeroizer/models/payroll/employee.rb index b6692974..6e8e9a66 100644 --- a/lib/xeroizer/models/payroll/employee.rb +++ b/lib/xeroizer/models/payroll/employee.rb @@ -41,6 +41,7 @@ class Employee < PayrollBase belongs_to :pay_template, :internal_name_singular => "pay_template", :model_name => "PayTemplate" belongs_to :opening_balances, :internal_name_singular => "opening_balance", :model_name => "OpeningBalances" has_many :super_memberships + has_many :leave_balances, :internal_name_singular => "leave_balance", model_name: "LeaveBalance" end diff --git a/lib/xeroizer/models/payroll/leave_balance.rb b/lib/xeroizer/models/payroll/leave_balance.rb new file mode 100644 index 00000000..33ed389c --- /dev/null +++ b/lib/xeroizer/models/payroll/leave_balance.rb @@ -0,0 +1,21 @@ +module Xeroizer + module Record + module Payroll + + class LeaveBalanceModel < PayrollBaseModel + + end + + # child of Employee + class LeaveBalance < PayrollBase + + string :leave_name + guid :leave_type_id + decimal :number_of_units + string :type_of_units + + end + + end + end +end diff --git a/test/stub_responses/records/payroll_employee-fb4ebd68-6568-41eb-96ab-628a0f54b4b8.xml b/test/stub_responses/records/payroll_employee-fb4ebd68-6568-41eb-96ab-628a0f54b4b8.xml index 0ebd36ac..33ea6b6c 100644 --- a/test/stub_responses/records/payroll_employee-fb4ebd68-6568-41eb-96ab-628a0f54b4b8.xml +++ b/test/stub_responses/records/payroll_employee-fb4ebd68-6568-41eb-96ab-628a0f54b4b8.xml @@ -142,6 +142,20 @@ 1234 + + + Annual Leave + 544d9292-4329-4512-bfff-a9f15236d776 + 81.2602 + Hours + + + Personal/Carer's Leave + 5fbec239-131c-4ebc-87c4-47db887a85d1 + 45.8302 + Hours + + \ No newline at end of file diff --git a/test/stub_responses/records/payroll_payroll_run-7c998e04-1cee-4a19-bfe6-3cbfd5cb9cea.xml b/test/stub_responses/records/payroll_pay_run-7c998e04-1cee-4a19-bfe6-3cbfd5cb9cea.xml similarity index 100% rename from test/stub_responses/records/payroll_payroll_run-7c998e04-1cee-4a19-bfe6-3cbfd5cb9cea.xml rename to test/stub_responses/records/payroll_pay_run-7c998e04-1cee-4a19-bfe6-3cbfd5cb9cea.xml diff --git a/test/stub_responses/records/payroll_payroll_run-e3bdb2f7-2b20-45e6-ac8d-ec67d17de9f4.xml b/test/stub_responses/records/payroll_pay_run-e3bdb2f7-2b20-45e6-ac8d-ec67d17de9f4.xml similarity index 100% rename from test/stub_responses/records/payroll_payroll_run-e3bdb2f7-2b20-45e6-ac8d-ec67d17de9f4.xml rename to test/stub_responses/records/payroll_pay_run-e3bdb2f7-2b20-45e6-ac8d-ec67d17de9f4.xml diff --git a/test/test_helper.rb b/test/test_helper.rb index 42f52522..c791eddd 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,5 +1,4 @@ require "rubygems" -require 'debugger' require 'test/unit' require 'mocha' require 'shoulda' diff --git a/test/unit/models/payroll/employees_test.rb b/test/unit/models/payroll/employees_test.rb index 3e21ffe1..adfc330e 100644 --- a/test/unit/models/payroll/employees_test.rb +++ b/test/unit/models/payroll/employees_test.rb @@ -23,5 +23,12 @@ def setup assert_not_nil employee.opening_balances assert_not_nil employee.opening_balances.opening_balance_date assert_equal 1, employee.opening_balances.earnings_lines.length + + assert_not_nil employee.leave_balances + assert_equal 2, employee.leave_balances.length + assert_not_nil employee.leave_balances.first.leave_name + assert_not_nil employee.leave_balances.first.leave_type_id + assert_not_nil employee.leave_balances.first.number_of_units + assert_not_nil employee.leave_balances.first.type_of_units end end From fda5eada1e798328eaaed5c2514e375a1f20ea9c Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Sun, 11 Dec 2016 13:21:02 +1000 Subject: [PATCH 36/82] add time off balance endpoint --- lib/xeroizer.rb | 2 +- lib/xeroizer/models/payroll/employee.rb | 17 +++++++------- .../models/payroll/time_off_balance.rb | 22 +++++++++++++++++++ 3 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 lib/xeroizer/models/payroll/time_off_balance.rb diff --git a/lib/xeroizer.rb b/lib/xeroizer.rb index 22be9be1..44dbcbad 100644 --- a/lib/xeroizer.rb +++ b/lib/xeroizer.rb @@ -45,7 +45,7 @@ 'leave_line', 'reimbursement_line', 'super_line', 'deduction_line', 'earnings_line', 'opening_balance', 'pay_run', 'settings', 'tracking_categories', 'employee_groups', 'timesheet_categories', 'account', 'tax_declaration', 'payslip', 'timesheet_earnings_line', 'tax_line', 'leave_accrual_line', 'superannuation_line', - 'leave_balance'].each do |payroll_model| + 'leave_balance', 'time_off_balance'].each do |payroll_model| require "xeroizer/models/payroll/#{payroll_model}" end diff --git a/lib/xeroizer/models/payroll/employee.rb b/lib/xeroizer/models/payroll/employee.rb index 6e8e9a66..1030ec11 100644 --- a/lib/xeroizer/models/payroll/employee.rb +++ b/lib/xeroizer/models/payroll/employee.rb @@ -1,15 +1,15 @@ module Xeroizer module Record module Payroll - + class EmployeeModel < PayrollBaseModel - + set_permissions :read, :write, :update - + end - + class Employee < PayrollBase - + set_primary_key :employee_id guid :employee_id @@ -34,17 +34,18 @@ class Employee < PayrollBase string :employee_group_name date :termination_date datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC' - + belongs_to :home_address, :internal_name_singular => "home_address", :model_name => "HomeAddress" belongs_to :tax_declaration, :internal_name_singular => "tax_declaration", :model_name => "TaxDeclaration" has_many :bank_accounts belongs_to :pay_template, :internal_name_singular => "pay_template", :model_name => "PayTemplate" belongs_to :opening_balances, :internal_name_singular => "opening_balance", :model_name => "OpeningBalances" has_many :super_memberships - has_many :leave_balances, :internal_name_singular => "leave_balance", model_name: "LeaveBalance" + has_many :leave_balances, :internal_name_singular => "leave_balance", model_name: "LeaveBalance" # https://developer.xero.com/documentation/payroll-api/leavebalances/ + has_many :time_off_balances, :internal_name_singular => "time_off_balance", model_name: "TimeOffBalance" # https://developer.xero.com/documentation/payroll-api-us/timeoff-balances/ end - end + end end end diff --git a/lib/xeroizer/models/payroll/time_off_balance.rb b/lib/xeroizer/models/payroll/time_off_balance.rb new file mode 100644 index 00000000..84616e04 --- /dev/null +++ b/lib/xeroizer/models/payroll/time_off_balance.rb @@ -0,0 +1,22 @@ +module Xeroizer + module Record + module Payroll + + class TimeOffBalanceModel < PayrollBaseModel + + end + + # child of Employee + # https://developer.xero.com/documentation/payroll-api-us/timeoff-balances/ + class TimeOffBalance < PayrollBase + + string :time_off_name + guid :time_off_type_id + decimal :number_of_units + string :type_of_units + + end + + end + end +end From fc5b6b0f77514b2b02ca302f55637277aa370238 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Sun, 11 Dec 2016 14:05:56 +1000 Subject: [PATCH 37/82] add earnings type model --- lib/xeroizer.rb | 2 +- lib/xeroizer/models/payroll/earnings_type.rb | 33 ++++++++++++++++++++ lib/xeroizer/models/payroll/pay_items.rb | 14 +++++---- 3 files changed, 42 insertions(+), 7 deletions(-) create mode 100644 lib/xeroizer/models/payroll/earnings_type.rb diff --git a/lib/xeroizer.rb b/lib/xeroizer.rb index 44dbcbad..4591bb13 100644 --- a/lib/xeroizer.rb +++ b/lib/xeroizer.rb @@ -45,7 +45,7 @@ 'leave_line', 'reimbursement_line', 'super_line', 'deduction_line', 'earnings_line', 'opening_balance', 'pay_run', 'settings', 'tracking_categories', 'employee_groups', 'timesheet_categories', 'account', 'tax_declaration', 'payslip', 'timesheet_earnings_line', 'tax_line', 'leave_accrual_line', 'superannuation_line', - 'leave_balance', 'time_off_balance'].each do |payroll_model| + 'leave_balance', 'time_off_balance', 'earnings_type'].each do |payroll_model| require "xeroizer/models/payroll/#{payroll_model}" end diff --git a/lib/xeroizer/models/payroll/earnings_type.rb b/lib/xeroizer/models/payroll/earnings_type.rb new file mode 100644 index 00000000..bc1c9f79 --- /dev/null +++ b/lib/xeroizer/models/payroll/earnings_type.rb @@ -0,0 +1,33 @@ +module Xeroizer + module Record + module Payroll + + class EarningsTypeModel < PayrollBaseModel + + end + + # https://developer.xero.com/documentation/payroll-api-us/pay-items/#EarningsTypes + class EarningsType < PayrollBase + + string :earnings_type + string :expense_account_code # http://developer.xero.com/api/Accounts + string :earnings_category # https://developer.xero.com/documentation/payroll-api-us/types-codes/#EarningsCategory + string :rate_type + string :type_of_units + string :earnings_type # http://developer.xero.com/payroll-api/types-and-codes/#EarningsTypes + + guid :earnings_rate_id + decimal :multiple + boolean :do_not_accrue_time_off + boolean :is_supplemental + decimal :amount + + datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC' + + validates_presence_of :earnings_type, :expense_account_code, :earnings_category + + end + + end + end +end diff --git a/lib/xeroizer/models/payroll/pay_items.rb b/lib/xeroizer/models/payroll/pay_items.rb index d1a27296..87662938 100644 --- a/lib/xeroizer/models/payroll/pay_items.rb +++ b/lib/xeroizer/models/payroll/pay_items.rb @@ -1,27 +1,29 @@ module Xeroizer module Record module Payroll - + class PayItemModel < PayrollBaseModel - + set_permissions :read, :write, :update set_standalone_model true set_xml_root_name 'PayItems' set_xml_node_name 'PayItems' end - + class PayItem < PayrollBase set_primary_key false - - has_many :earnings_rates + + has_many :earnings_rates # AU has_many :deduction_types has_many :leave_types has_many :reimbursement_types + has_many :earnings_types # US + end - end + end end end From 63e75982de896d799685a75ffc26a248e0b6e5a7 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Sun, 11 Dec 2016 14:32:11 +1000 Subject: [PATCH 38/82] add US timesheet fields --- lib/xeroizer/models/payroll/timesheet_line.rb | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/xeroizer/models/payroll/timesheet_line.rb b/lib/xeroizer/models/payroll/timesheet_line.rb index 56e5d181..6ea58283 100644 --- a/lib/xeroizer/models/payroll/timesheet_line.rb +++ b/lib/xeroizer/models/payroll/timesheet_line.rb @@ -1,22 +1,27 @@ module Xeroizer module Record module Payroll - + class TimesheetLineModel < PayrollBaseModel - + end - + class TimesheetLine < PayrollBase + # AU guid :earnings_rate_id guid :tracking_item_id + # USA + guid :earnings_type_id + guid :work_location_id + has_array :number_of_units, :api_child_name => 'NumberOfUnit' datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC' end - end + end end end From 45544051975a2e37187085722fc7ce5a451f5ccb Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Mon, 16 Jan 2017 17:12:34 +1000 Subject: [PATCH 39/82] https://developer.xero.com/documentation/auth-and-limits/entrust-certificate-deprecation/ --- lib/xeroizer/partner_application.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/xeroizer/partner_application.rb b/lib/xeroizer/partner_application.rb index 12cefba4..a044f844 100644 --- a/lib/xeroizer/partner_application.rb +++ b/lib/xeroizer/partner_application.rb @@ -19,8 +19,8 @@ class PartnerApplication < GenericApplication # @return [PartnerApplication] instance of PrivateApplication def initialize(consumer_key, consumer_secret, path_to_private_key, ssl_client_cert, ssl_client_key, options = {}) default_options = { - :xero_url_prefix => 'https://api-partner.network.xero.com', - :site => 'https://api-partner.network.xero.com', + :xero_url_prefix => 'https://api.xero.com', + :site => 'https://api.xero.com', :authorize_url => 'https://api.xero.com/oauth/Authorize', :signature_method => 'RSA-SHA1' } From 66124f059a7328ca97fc99c69ec14570b5fdaad4 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Mon, 1 May 2017 16:43:07 +1000 Subject: [PATCH 40/82] add missing TFN field --- lib/xeroizer/models/payroll/tax_declaration.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/xeroizer/models/payroll/tax_declaration.rb b/lib/xeroizer/models/payroll/tax_declaration.rb index 09ddb64d..cba15ae8 100644 --- a/lib/xeroizer/models/payroll/tax_declaration.rb +++ b/lib/xeroizer/models/payroll/tax_declaration.rb @@ -1,15 +1,16 @@ module Xeroizer module Record module Payroll - + class TaxDeclarationModel < PayrollBaseModel - + end - + class TaxDeclaration < PayrollBase - + guid :employee_id string :employment_basis + string :tax_file_number string :tfn_exemption_type, :api_name => 'TFNExemptionType' boolean :australian_resident_for_tax_purposes boolean :tax_free_threshold_claimed @@ -22,6 +23,6 @@ class TaxDeclaration < PayrollBase end - end + end end end From ee947c9c937bd62ab4c3aa30cf8617a48e9cc320 Mon Sep 17 00:00:00 2001 From: David Buchan-Swanson Date: Mon, 15 May 2017 11:38:37 +1000 Subject: [PATCH 41/82] add the super fund class --- lib/xeroizer/models/payroll/super_fund.rb | 28 +++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 lib/xeroizer/models/payroll/super_fund.rb diff --git a/lib/xeroizer/models/payroll/super_fund.rb b/lib/xeroizer/models/payroll/super_fund.rb new file mode 100644 index 00000000..dccf8121 --- /dev/null +++ b/lib/xeroizer/models/payroll/super_fund.rb @@ -0,0 +1,28 @@ +module Xeroizer + module Record + module Payroll + + class SuperFund < PayrollBaseModel + + end + + class SuperFund < PayrollBase + + guid :super_fund_id + string :type + string :abn, api_name: "ABN" + + # this is for Regulated funds + string :usi, api_name: "USI" + + # this is for self-managed funds + string :name + string :bsb, api_name: "BSB" + string :account_number + string :account_name + string :electronic_service_address + end + + end + end +end From f84689e0f03d33b19db6b5c64e14b91b31b8af9b Mon Sep 17 00:00:00 2001 From: David Buchan-Swanson Date: Mon, 15 May 2017 11:57:35 +1000 Subject: [PATCH 42/82] add some comments about the data --- lib/xeroizer/models/payroll/super_fund.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/xeroizer/models/payroll/super_fund.rb b/lib/xeroizer/models/payroll/super_fund.rb index dccf8121..df80dc6b 100644 --- a/lib/xeroizer/models/payroll/super_fund.rb +++ b/lib/xeroizer/models/payroll/super_fund.rb @@ -6,6 +6,7 @@ class SuperFund < PayrollBaseModel end + # https://developer.xero.com/documentation/payroll-api/superfunds class SuperFund < PayrollBase guid :super_fund_id From f5b98a229fec21fdc79385d687cc76543b961375 Mon Sep 17 00:00:00 2001 From: David Buchan-Swanson Date: Mon, 15 May 2017 16:03:56 +1000 Subject: [PATCH 43/82] add additional data to make the model work --- lib/xeroizer.rb | 2 +- lib/xeroizer/models/payroll/employee.rb | 2 +- lib/xeroizer/models/payroll/super_fund.rb | 6 +++--- lib/xeroizer/payroll_application.rb | 7 ++++--- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/xeroizer.rb b/lib/xeroizer.rb index 3027f91f..115fee7b 100644 --- a/lib/xeroizer.rb +++ b/lib/xeroizer.rb @@ -45,7 +45,7 @@ 'leave_line', 'reimbursement_line', 'super_line', 'deduction_line', 'earnings_line', 'opening_balance', 'pay_run', 'settings', 'tracking_categories', 'employee_groups', 'timesheet_categories', 'account', 'tax_declaration', 'payslip', 'timesheet_earnings_line', 'tax_line', 'leave_accrual_line', 'superannuation_line', - 'leave_balance', 'time_off_balance', 'earnings_type'].each do |payroll_model| + 'leave_balance', 'time_off_balance', 'earnings_type', 'super_fund'].each do |payroll_model| require "xeroizer/models/payroll/#{payroll_model}" end diff --git a/lib/xeroizer/models/payroll/employee.rb b/lib/xeroizer/models/payroll/employee.rb index 1030ec11..3f246c3c 100644 --- a/lib/xeroizer/models/payroll/employee.rb +++ b/lib/xeroizer/models/payroll/employee.rb @@ -40,7 +40,7 @@ class Employee < PayrollBase has_many :bank_accounts belongs_to :pay_template, :internal_name_singular => "pay_template", :model_name => "PayTemplate" belongs_to :opening_balances, :internal_name_singular => "opening_balance", :model_name => "OpeningBalances" - has_many :super_memberships + has_many :super_memberships, :internal_name_singular => "super_membership", :model_name => "SuperMembership" has_many :leave_balances, :internal_name_singular => "leave_balance", model_name: "LeaveBalance" # https://developer.xero.com/documentation/payroll-api/leavebalances/ has_many :time_off_balances, :internal_name_singular => "time_off_balance", model_name: "TimeOffBalance" # https://developer.xero.com/documentation/payroll-api-us/timeoff-balances/ diff --git a/lib/xeroizer/models/payroll/super_fund.rb b/lib/xeroizer/models/payroll/super_fund.rb index df80dc6b..5baf098a 100644 --- a/lib/xeroizer/models/payroll/super_fund.rb +++ b/lib/xeroizer/models/payroll/super_fund.rb @@ -2,15 +2,15 @@ module Xeroizer module Record module Payroll - class SuperFund < PayrollBaseModel - + class SuperFundModel < PayrollBaseModel + set_permissions :read, :write, :update end # https://developer.xero.com/documentation/payroll-api/superfunds class SuperFund < PayrollBase guid :super_fund_id - string :type + string :type # http://developer.xero.com/documentation/payroll-api/types-and-codes#SuperFundsTypes string :abn, api_name: "ABN" # this is for Regulated funds diff --git a/lib/xeroizer/payroll_application.rb b/lib/xeroizer/payroll_application.rb index 08cde9e6..43702603 100644 --- a/lib/xeroizer/payroll_application.rb +++ b/lib/xeroizer/payroll_application.rb @@ -1,6 +1,6 @@ module Xeroizer class PayrollApplication - + attr_reader :application extend Forwardable @@ -21,7 +21,7 @@ def self.record(record_type) instance_variable_set(var_name, Xeroizer::Record::Payroll.const_get("#{record_type}Model".to_sym).new(self.application, record_type.to_s)) end instance_variable_get(var_name) - end + end end record :Employee @@ -32,7 +32,8 @@ def self.record(record_type) record :PayRun record :Payslip record :Setting - + record :SuperFund + def initialize(application) @application = application end From 559308061c64b9ef13b5f02ef73e2eee37d483f9 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Wed, 26 Jul 2017 13:50:09 +0800 Subject: [PATCH 44/82] use json format if requested at client creation --- lib/xeroizer/generic_application.rb | 7 ++-- lib/xeroizer/http.rb | 3 +- lib/xeroizer/record/base_model_http_proxy.rb | 35 ++++++++++---------- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/lib/xeroizer/generic_application.rb b/lib/xeroizer/generic_application.rb index 728a0eae..2030c414 100644 --- a/lib/xeroizer/generic_application.rb +++ b/lib/xeroizer/generic_application.rb @@ -6,9 +6,10 @@ class GenericApplication include Http extend Record::ApplicationHelper - attr_writer :xero_url_prefix, :xero_url_suffix + attr_writer :xero_url_prefix, :xero_url_suffix, :api_format attr_reader :client, :xero_url, :logger, :rate_limit_sleep, :rate_limit_max_attempts, - :default_headers, :unitdp, :before_request, :after_request, :nonce_used_max_attempts + :default_headers, :unitdp, :before_request, :after_request, :nonce_used_max_attempts, + :api_format extend Forwardable def_delegators :client, :access_token @@ -58,6 +59,7 @@ class GenericApplication # @see PrivateApplication # @see PartnerApplication def initialize(consumer_key, consumer_secret, options = {}) + @api_format = options[:api_format] || :xml @xero_url_prefix = options[:xero_url_prefix] || "https://api.xero.com" @xero_url_suffix = options[:xero_url_suffix] || "api.xro/2.0" @rate_limit_sleep = options[:rate_limit_sleep] || false @@ -74,6 +76,7 @@ def initialize(consumer_key, consumer_secret, options = {}) def payroll(options = {}) xero_client = self.clone xero_client.xero_url_suffix = options[:xero_url_suffix] || "payroll.xro/1.0" + xero_client.api_format = options[:api_format] || :xml @payroll ||= PayrollApplication.new(xero_client) end diff --git a/lib/xeroizer/http.rb b/lib/xeroizer/http.rb index 5d95b414..c802a2e0 100644 --- a/lib/xeroizer/http.rb +++ b/lib/xeroizer/http.rb @@ -19,7 +19,8 @@ class BadResponse < StandardError; end ACCEPT_MIME_MAP = { :pdf => 'application/pdf', - :json => 'application/json' + :json => 'application/json', + :xml => 'application/xml', } # Shortcut method for #http_request with `method` = :get. diff --git a/lib/xeroizer/record/base_model_http_proxy.rb b/lib/xeroizer/record/base_model_http_proxy.rb index 301fb1ee..bd0199ac 100644 --- a/lib/xeroizer/record/base_model_http_proxy.rb +++ b/lib/xeroizer/record/base_model_http_proxy.rb @@ -1,18 +1,18 @@ -require 'xeroizer/application_http_proxy' +require 'xeroizer/application_http_proxy' module Xeroizer module Record module BaseModelHttpProxy - + def self.included(base) base.send :include, Xeroizer::ApplicationHttpProxy base.send :include, InstanceMethods end - + module InstanceMethods - + protected - + # Parse parameters for GET requests. def parse_params(options) params = {} @@ -22,15 +22,16 @@ def parse_params(options) if options[:where] params[:where] = case options[:where] - when String then options[:where] + when String then options[:where] when Hash then parse_where_hash(options[:where]) end end params[:offset] = options[:offset] if options[:offset] params[:page] = options[:page] if options[:page] + params[:response] = options[:api_format] || @application.api_format params end - + # Parse the :where part of the options for GET parameters and construct a valid # .Net version of the criteria to pass to Xero. # @@ -46,14 +47,14 @@ def parse_where_hash(where) (attribute_name, expression) = extract_expression_from_attribute_name(key) (field_name, field) = model_class.fields.find { | k, v | v[:internal_name] == attribute_name } if field - conditions << where_condition_part(field, expression, value) + conditions << where_condition_part(field, expression, value) else raise InvalidAttributeInWhere.new(model_name, attribute_name) end end conditions.map { | (attr, expression, value) | "#{attr}#{expression}#{value}"}.join('&&') end - + # Extract the attribute name and expression from the attribute. # # @return [Array] containing [actual_attribute_name, expression] @@ -64,37 +65,37 @@ def extract_expression_from_attribute_name(key) key.to_s.gsub(/(_is_not|\<\>)$/, '').to_sym, '<>' ] - + when /(_is_greater_than|\>)$/ [ key.to_s.gsub(/(_is_greater_than|\>)$/, '').to_sym, '>' ] - + when /(_is_greater_than_or_equal_to|\>\=)$/ [ key.to_s.gsub(/(_is_greater_than_or_equal_to|\>\=)$/, '').to_sym, '>=' ] - + when /(_is_less_than|\<)$/ [ key.to_s.gsub(/(_is_less_than|\<)$/, '').to_sym, '<' ] - + when /(_is_less_than_or_equal_to|\<\=)$/ [ key.to_s.gsub(/(_is_less_than_or_equal_to|\<\=)$/, '').to_sym, '<=' ] - + else [key, '=='] - + end end - + # Creates a condition part array containing the: # * Field's API name # * Expression @@ -115,7 +116,7 @@ def where_condition_part(field, expression, value) end end - + end end end From bfba7da26f05bfba7f42cd58460073b6a8181911 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Wed, 26 Jul 2017 14:35:32 +0800 Subject: [PATCH 45/82] add support for getting UK employee fields. ignoring address for now - TODO figure out how to integrate that into the homeaddress model we already have --- lib/xeroizer/models/payroll/employee.rb | 2 ++ lib/xeroizer/record/base.rb | 14 +++++--- lib/xeroizer/record/base_model.rb | 45 +++++++++++++++++++------ 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/lib/xeroizer/models/payroll/employee.rb b/lib/xeroizer/models/payroll/employee.rb index 3f246c3c..1c978ea0 100644 --- a/lib/xeroizer/models/payroll/employee.rb +++ b/lib/xeroizer/models/payroll/employee.rb @@ -33,6 +33,8 @@ class Employee < PayrollBase guid :payroll_calendar_id string :employee_group_name date :termination_date + string :national_insurance_number # UK + guid :pay_run_calendar_id # UK datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC' belongs_to :home_address, :internal_name_singular => "home_address", :model_name => "HomeAddress" diff --git a/lib/xeroizer/record/base.rb b/lib/xeroizer/record/base.rb index a55dab7b..fbc73981 100644 --- a/lib/xeroizer/record/base.rb +++ b/lib/xeroizer/record/base.rb @@ -20,7 +20,7 @@ class Base attr_writer :api_method_for_creating attr_writer :api_method_for_updating - + attr_accessor :paged_record_downloaded include ModelDefinitionHelper @@ -34,8 +34,12 @@ class << self def build(attributes, parent) record = new(parent) attributes.each do | key, value | - attr = record.respond_to?("#{key}=") ? key : record.class.fields[key][:internal_name] - record.send("#{attr}=", value) + attr_to_set = if record.respond_to?("#{key}=") + key + elsif record.class.fields.key?(key) + record.class.fields[key][:internal_name] + end + record.send("#{attr_to_set}=", value) if attr_to_set end record end @@ -153,14 +157,14 @@ def api_method_for_creating def api_method_for_updating @api_method_for_updating || :http_post end - + protected # Attempt to create a new record. def create request = to_xml log "[CREATE SENT] (#{__FILE__}:#{__LINE__}) #{request}" - + response = parent.send(api_method_for_creating, request) log "[CREATE RECEIVED] (#{__FILE__}:#{__LINE__}) #{response}" diff --git a/lib/xeroizer/record/base_model.rb b/lib/xeroizer/record/base_model.rb index 6ffc62b8..ec8f4cd1 100644 --- a/lib/xeroizer/record/base_model.rb +++ b/lib/xeroizer/record/base_model.rb @@ -21,9 +21,9 @@ class InvalidPermissionError < StandardError class_inheritable_attributes :standalone_model class_inheritable_attributes :before_padding class_inheritable_attributes :after_padding - + DEFAULT_RECORDS_PER_BATCH_SAVE = 2000 - + include BaseModelHttpProxy attr_reader :application @@ -89,7 +89,7 @@ def set_api_response_padding(padding) def pad_api_response? self.before_padding && self.after_padding end - + end public @@ -139,8 +139,8 @@ def create(attributes = {}) # Retreive full record list for this model. def all(options = {}) raise MethodNotAllowed.new(self, :all) unless self.class.permissions[:read] - response_xml = http_get(parse_params(options)) - response = parse_response(response_xml, options) + response_body = http_get(parse_params(options)) + response = parse_response(response_body, options) response_items = response.response_items || [] end @@ -155,8 +155,8 @@ def first(options = {}) # Retrieve record matching the passed in ID. def find(id, options = {}) raise MethodNotAllowed.new(self, :all) unless self.class.permissions[:read] - response_xml = @application.http_get(@application.client, "#{url}/#{CGI.escape(id)}", options) - response = parse_response(response_xml, options) + response_body = @application.http_get(@application.client, "#{url}/#{CGI.escape(id)}", options) + response = parse_response(response_body, options) result = response.response_items.first if response.response_items.is_a?(Array) result.complete_record_downloaded = true if result result @@ -202,8 +202,34 @@ def batch_save(chunk_size = DEFAULT_RECORDS_PER_BATCH_SAVE) end end - def parse_response(response_xml, options = {}) - Response.parse(response_xml, options) do | response, elements, response_model_name | + def parse_response(response_body, options = {}) + format = options[:api_format] || @application.api_format + case format + when :json + parse_json_response(response_body, options) + else + parse_xml_response(response_body, options) + end + end + + protected + def parse_json_response(response_body, options = {}) + json = ::JSON.parse(response_body) + response = Response.new + + iterable = json[self.model_name.downcase.pluralize] || json[self.model_name.downcase] + iterable = [iterable] if iterable.is_a?(Hash) + + iterable.each {|object| + object = object.map {|key, value| [key.underscore, value]}.to_h + response.response_items << self.model_class.build(object, self) + } + + response + end + + def parse_xml_response(response_body, options = {}) + Response.parse(response_body, options) do | response, elements, response_model_name | if self.class.pad_api_response? @response = response parse_records(response, Nokogiri::XML("#{self.class.before_padding}#{elements.to_xml}#{self.class.after_padding}").root.elements, paged_records_requested?(options), (options[:base_module] || Xeroizer::Record)) @@ -217,7 +243,6 @@ def parse_response(response_xml, options = {}) end end - protected def paged_records_requested?(options) options.has_key?(:page) and options[:page].to_i >= 0 end From 62d8351969acbb64a3ba89d88321c016329ab4bb Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Tue, 26 Sep 2017 14:20:01 +1000 Subject: [PATCH 46/82] initial support for getting UK timesheets --- lib/xeroizer/models/payroll/timesheet_line.rb | 3 ++ lib/xeroizer/record/base_model.rb | 15 ++++++++-- .../record/record_association_helper.rb | 28 +++++++++++-------- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/lib/xeroizer/models/payroll/timesheet_line.rb b/lib/xeroizer/models/payroll/timesheet_line.rb index 6ea58283..e68d9d68 100644 --- a/lib/xeroizer/models/payroll/timesheet_line.rb +++ b/lib/xeroizer/models/payroll/timesheet_line.rb @@ -18,6 +18,9 @@ class TimesheetLine < PayrollBase has_array :number_of_units, :api_child_name => 'NumberOfUnit' + # UK + datetime :date + datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC' end diff --git a/lib/xeroizer/record/base_model.rb b/lib/xeroizer/record/base_model.rb index ec8f4cd1..25b13b07 100644 --- a/lib/xeroizer/record/base_model.rb +++ b/lib/xeroizer/record/base_model.rb @@ -221,8 +221,19 @@ def parse_json_response(response_body, options = {}) iterable = [iterable] if iterable.is_a?(Hash) iterable.each {|object| - object = object.map {|key, value| [key.underscore, value]}.to_h - response.response_items << self.model_class.build(object, self) + object = object.map {|key, value| [key.underscore.to_sym, value]}.to_h + response_object = self.model_class.build(object, self) + self.model_class.fields.each {|field, field_props| + if field_props[:type] == :has_many + response_object[field] = [] + object[field].each {|child_object| + model_class_name = field_props[:api_child_name].to_sym # TimesheetLine + model_klass_obj = response_object.new_model_class(model_class_name) + response_object[field] << model_klass_obj.model_class.build(child_object.map {|key, value| [key.underscore.to_sym, value]}.to_h, model_klass_obj) + } + end + } + response.response_items << response_object } response diff --git a/lib/xeroizer/record/record_association_helper.rb b/lib/xeroizer/record/record_association_helper.rb index 67a570e8..872f333a 100644 --- a/lib/xeroizer/record/record_association_helper.rb +++ b/lib/xeroizer/record/record_association_helper.rb @@ -36,7 +36,7 @@ def belongs_to(field_name, options = {}) def has_array(field_name, options = {}) internal_field_name = options[:internal_name] || field_name internal_plural_field_name = options[:internal_name_plural] || internal_field_name.to_s - + define_association_attribute(field_name, internal_field_name, :has_array, options) define_method "add_to_#{internal_plural_field_name}" do | *args | @@ -45,7 +45,7 @@ def has_array(field_name, options = {}) # The record's parent instance for this current application. model_parent = new_model_class(model_name) - + # The class of this record. record_class = (options[:base_module] || Xeroizer::Record).const_get(model_name) @@ -55,15 +55,15 @@ def has_array(field_name, options = {}) values = [] if args.size == 1 && args.first.is_a?(Array) values = args.first - elsif args.size > 0 + elsif args.size > 0 values = args else raise StandardError.new("Invalid arguments for #{self.class.name}#add_#{internal_singular_field_name}(#{args.inspect}).") end - + # Ensure that complete record is downloaded before adding new records self.send(field_name) - + # Add each value. last_record = nil values.each do |value| @@ -74,18 +74,18 @@ def has_array(field_name, options = {}) self.parent.mark_dirty(self) if self.parent last_record = record end - + last_record end end - + alias_method :has_one, :belongs_to def has_many(field_name, options = {}) internal_field_name = options[:internal_name] || field_name internal_singular_field_name = options[:internal_name_singular] || internal_field_name.to_s.singularize - + define_association_attribute(field_name, internal_field_name, :has_many, options) # Create an #add_record_name method to build the record and add to the attributes. @@ -148,7 +148,7 @@ def define_association_attribute(field_name, internal_field_name, association_ty when :has_many self.attributes[field_name] = [] self.send("add_#{internal_singular_field_name}".to_sym, value) - when :belongs_to + when :belongs_to self.attributes[field_name] = (options[:base_module] || Xeroizer::Record).const_get(model_name).build(value, new_model_class(model_name)) end @@ -163,12 +163,18 @@ def define_association_attribute(field_name, internal_field_name, association_ty end when record_class - self.attributes[field_name] = self.class.value_if_nil(association_type, value) when NilClass self.attributes[field_name] = [] + when Float + if record_class.fields.count == 1 && record_class.fields.keys.first == :value + self.attributes[field_name] = self.class.value_if_nil(association_type, value) + else + raise AssociationTypeMismatch.new(record_class, value.class) + end + else raise AssociationTypeMismatch.new(record_class, value.class) end @@ -196,7 +202,7 @@ def value_if_nil(association_type, boxed_value = nil) boxed_value end end - + end end From 6144fab3db699c5c7c380db0550ea701cadff536 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Wed, 27 Sep 2017 10:59:16 +1000 Subject: [PATCH 47/82] UK exposes an earnings rates API --- lib/xeroizer/models/payroll/earnings_rate.rb | 10 ++++++---- lib/xeroizer/payroll_application.rb | 1 + lib/xeroizer/record/base_model.rb | 9 +++++---- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/xeroizer/models/payroll/earnings_rate.rb b/lib/xeroizer/models/payroll/earnings_rate.rb index 77089bad..8cd76ad2 100644 --- a/lib/xeroizer/models/payroll/earnings_rate.rb +++ b/lib/xeroizer/models/payroll/earnings_rate.rb @@ -1,11 +1,13 @@ module Xeroizer module Record module Payroll - + class EarningsRateModel < PayrollBaseModel - + + set_permissions :read, :write, :update + end - + class EarningsRate < PayrollBase string :name @@ -28,6 +30,6 @@ class EarningsRate < PayrollBase end - end + end end end diff --git a/lib/xeroizer/payroll_application.rb b/lib/xeroizer/payroll_application.rb index 43702603..bb156a32 100644 --- a/lib/xeroizer/payroll_application.rb +++ b/lib/xeroizer/payroll_application.rb @@ -33,6 +33,7 @@ def self.record(record_type) record :Payslip record :Setting record :SuperFund + record :EarningsRate # UK def initialize(application) @application = application diff --git a/lib/xeroizer/record/base_model.rb b/lib/xeroizer/record/base_model.rb index 25b13b07..793edce1 100644 --- a/lib/xeroizer/record/base_model.rb +++ b/lib/xeroizer/record/base_model.rb @@ -1,4 +1,5 @@ require 'xeroizer/record/base_model_http_proxy' +require 'active_support/inflector' module Xeroizer module Record @@ -138,7 +139,7 @@ def create(attributes = {}) # Retreive full record list for this model. def all(options = {}) - raise MethodNotAllowed.new(self, :all) unless self.class.permissions[:read] + raise MethodNotAllowed.new(self, :all) unless self.class.permissions && self.class.permissions[:read] response_body = http_get(parse_params(options)) response = parse_response(response_body, options) response_items = response.response_items || [] @@ -147,14 +148,14 @@ def all(options = {}) # Helper method to retrieve just the first element from # the full record list. def first(options = {}) - raise MethodNotAllowed.new(self, :all) unless self.class.permissions[:read] + raise MethodNotAllowed.new(self, :all) unless self.class.permissions && self.class.permissions[:read] result = all(options) result.first if result.is_a?(Array) end # Retrieve record matching the passed in ID. def find(id, options = {}) - raise MethodNotAllowed.new(self, :all) unless self.class.permissions[:read] + raise MethodNotAllowed.new(self, :all) unless self.class.permissions && self.class.permissions[:read] response_body = @application.http_get(@application.client, "#{url}/#{CGI.escape(id)}", options) response = parse_response(response_body, options) result = response.response_items.first if response.response_items.is_a?(Array) @@ -217,7 +218,7 @@ def parse_json_response(response_body, options = {}) json = ::JSON.parse(response_body) response = Response.new - iterable = json[self.model_name.downcase.pluralize] || json[self.model_name.downcase] + iterable = json[self.model_name.camelize(:lower).pluralize] || json[self.model_name.camelize(:lower)] iterable = [iterable] if iterable.is_a?(Hash) iterable.each {|object| From 805bbade9859085dec5124572cdf573e74d72027 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Wed, 27 Sep 2017 11:14:33 +1000 Subject: [PATCH 48/82] fix nil ref --- lib/xeroizer/record/base_model.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/xeroizer/record/base_model.rb b/lib/xeroizer/record/base_model.rb index 793edce1..2cdd2008 100644 --- a/lib/xeroizer/record/base_model.rb +++ b/lib/xeroizer/record/base_model.rb @@ -225,7 +225,7 @@ def parse_json_response(response_body, options = {}) object = object.map {|key, value| [key.underscore.to_sym, value]}.to_h response_object = self.model_class.build(object, self) self.model_class.fields.each {|field, field_props| - if field_props[:type] == :has_many + if field_props[:type] == :has_many && object[field] response_object[field] = [] object[field].each {|child_object| model_class_name = field_props[:api_child_name].to_sym # TimesheetLine From a35333a61b247bc7609f414410c7c3c5031a183b Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Wed, 27 Sep 2017 11:57:54 +1000 Subject: [PATCH 49/82] support JSON put/post --- lib/xeroizer/application_http_proxy.rb | 15 +++++++++------ lib/xeroizer/record/base.rb | 12 ++++++++---- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/lib/xeroizer/application_http_proxy.rb b/lib/xeroizer/application_http_proxy.rb index a758c0a0..5ec0d14e 100644 --- a/lib/xeroizer/application_http_proxy.rb +++ b/lib/xeroizer/application_http_proxy.rb @@ -1,30 +1,33 @@ module Xeroizer module ApplicationHttpProxy - + def self.included(base) base.send :include, InstanceMethods end - + module InstanceMethods - + # URL end-point for this model. def url @application.xero_url + '/' + api_controller_name end def http_get(extra_params = {}) + extra_params.reverse_merge!(response: application.api_format) application.http_get(application.client, url, extra_params) end def http_put(xml, extra_params = {}) + extra_params.reverse_merge!(response: application.api_format) application.http_put(application.client, url, xml, extra_params) end def http_post(xml, extra_params = {}) + extra_params.reverse_merge!(response: application.api_format) application.http_post(application.client, url, xml, extra_params) end - + end - + end -end \ No newline at end of file +end diff --git a/lib/xeroizer/record/base.rb b/lib/xeroizer/record/base.rb index fbc73981..4a162747 100644 --- a/lib/xeroizer/record/base.rb +++ b/lib/xeroizer/record/base.rb @@ -162,10 +162,10 @@ def api_method_for_updating # Attempt to create a new record. def create - request = to_xml + request = json? ? to_json : to_xml log "[CREATE SENT] (#{__FILE__}:#{__LINE__}) #{request}" - response = parent.send(api_method_for_creating, request) + response = parent.send(api_method_for_creating, request, (json? ? {raw_body: true} : {})) log "[CREATE RECEIVED] (#{__FILE__}:#{__LINE__}) #{response}" @@ -178,17 +178,21 @@ def update raise RecordKeyMustBeDefined.new(self.class.possible_primary_keys) end - request = to_xml + request = json? ? to_json : to_xml log "[UPDATE SENT] (#{__FILE__}:#{__LINE__}) \r\n#{request}" - response = parent.send(api_method_for_updating, request) + response = parent.send(api_method_for_updating, request, (json? ? {raw_body: true} : {})) log "[UPDATE RECEIVED] (#{__FILE__}:#{__LINE__}) \r\n#{response}" parse_save_response(response) end + def json? + parent.application.api_format == :json + end + # Parse the response from a create/update request. def parse_save_response(response_xml) response = parent.parse_response(response_xml) From 26bc1145d9306874a7f31efecb895259701cc52d Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Tue, 3 Oct 2017 13:36:15 +1000 Subject: [PATCH 50/82] don't add raw_body=true to URL --- lib/xeroizer/http.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/xeroizer/http.rb b/lib/xeroizer/http.rb index c802a2e0..65b85423 100644 --- a/lib/xeroizer/http.rb +++ b/lib/xeroizer/http.rb @@ -84,6 +84,8 @@ def http_request(client, method, url, body, params = {}) headers['Accept'] = "application/xml" end + raw_body = params.delete(:raw_body) ? body : {:xml => body} + if params.any? url += "?" + params.map {|key,value| "#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s)}"}.join("&") end @@ -99,8 +101,6 @@ def http_request(client, method, url, body, params = {}) attempts += 1 logger.info("XeroGateway Request: #{method.to_s.upcase} #{uri.request_uri}") if self.logger - raw_body = params.delete(:raw_body) ? body : {:xml => body} - response = case method when :get then client.get(uri.request_uri, headers) when :post then client.post(uri.request_uri, raw_body, headers) From e50be7f4360e4dda11eebf1210410ba576af5fc5 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Tue, 3 Oct 2017 14:12:04 +1000 Subject: [PATCH 51/82] add support for JSON PUT/POST --- lib/xeroizer/record/base.rb | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/xeroizer/record/base.rb b/lib/xeroizer/record/base.rb index 4a162747..c24f75cb 100644 --- a/lib/xeroizer/record/base.rb +++ b/lib/xeroizer/record/base.rb @@ -3,6 +3,7 @@ require 'xeroizer/record/validation_helper' require 'xeroizer/record/xml_helper' require 'xeroizer/logging' +require 'active_support/inflector' module Xeroizer module Record @@ -162,10 +163,10 @@ def api_method_for_updating # Attempt to create a new record. def create - request = json? ? to_json : to_xml + request = json? ? to_api_json : to_xml log "[CREATE SENT] (#{__FILE__}:#{__LINE__}) #{request}" - response = parent.send(api_method_for_creating, request, (json? ? {raw_body: true} : {})) + response = parent.send(api_method_for_creating, request, extra_params_for_create_or_update) log "[CREATE RECEIVED] (#{__FILE__}:#{__LINE__}) #{response}" @@ -178,17 +179,28 @@ def update raise RecordKeyMustBeDefined.new(self.class.possible_primary_keys) end - request = json? ? to_json : to_xml + request = json? ? to_api_json : to_xml log "[UPDATE SENT] (#{__FILE__}:#{__LINE__}) \r\n#{request}" - response = parent.send(api_method_for_updating, request, (json? ? {raw_body: true} : {})) + response = parent.send(api_method_for_updating, request, extra_params_for_create_or_update) log "[UPDATE RECEIVED] (#{__FILE__}:#{__LINE__}) \r\n#{response}" parse_save_response(response) end + def extra_params_for_create_or_update + json? ? {raw_body: true, content_type: "application/json"} : {} + end + + def to_api_json + attrs = self.attributes.reject {|k, v| k == :parent }.map do |k, v| + [k.to_s.camelize(:lower), v.kind_of?(Array) ? v.map(&:to_h) : (v.respond_to?(:to_api_json) ? v.to_api_json : v)] + end + Hash[attrs].to_json + end + def json? parent.application.api_format == :json end From 206a934602645d8b92cc183954026e3735d36581 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Wed, 4 Oct 2017 13:50:34 +1000 Subject: [PATCH 52/82] UK calls it payroll calendar ID --- lib/xeroizer/models/payroll/timesheet.rb | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/xeroizer/models/payroll/timesheet.rb b/lib/xeroizer/models/payroll/timesheet.rb index da161709..65211da6 100644 --- a/lib/xeroizer/models/payroll/timesheet.rb +++ b/lib/xeroizer/models/payroll/timesheet.rb @@ -1,32 +1,33 @@ module Xeroizer module Record module Payroll - + class TimesheetModel < PayrollBaseModel - + set_permissions :read, :write, :update - + end - + class Timesheet < PayrollBase - + set_primary_key :timesheet_id guid :timesheet_id guid :employee_id + guid :payroll_calendar_id # UK date :start_date date :end_date decimal :hours string :status datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC' - + has_many :timesheet_lines validates_presence_of :start_date, :end_date, :employee_id end - end + end end end From c5ef3b669c6a94ce58b967ec1b978440fdd70dbc Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Wed, 4 Oct 2017 14:40:32 +1000 Subject: [PATCH 53/82] add support to C/U timesheet lines --- lib/xeroizer/application_http_proxy.rb | 10 +++++----- .../models/payroll/timesheet_earnings_line.rb | 8 ++++---- lib/xeroizer/models/payroll/timesheet_line.rb | 7 +++++++ lib/xeroizer/record/base.rb | 14 ++++++++++++-- 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/lib/xeroizer/application_http_proxy.rb b/lib/xeroizer/application_http_proxy.rb index 5ec0d14e..74684630 100644 --- a/lib/xeroizer/application_http_proxy.rb +++ b/lib/xeroizer/application_http_proxy.rb @@ -8,23 +8,23 @@ def self.included(base) module InstanceMethods # URL end-point for this model. - def url - @application.xero_url + '/' + api_controller_name + def url(suffix = nil) + @application.xero_url + '/' + (suffix || api_controller_name) end def http_get(extra_params = {}) extra_params.reverse_merge!(response: application.api_format) - application.http_get(application.client, url, extra_params) + application.http_get(application.client, url(extra_params.delete(:url)), extra_params) end def http_put(xml, extra_params = {}) extra_params.reverse_merge!(response: application.api_format) - application.http_put(application.client, url, xml, extra_params) + application.http_put(application.client, url(extra_params.delete(:url)), xml, extra_params) end def http_post(xml, extra_params = {}) extra_params.reverse_merge!(response: application.api_format) - application.http_post(application.client, url, xml, extra_params) + application.http_post(application.client, url(extra_params.delete(:url)), xml, extra_params) end end diff --git a/lib/xeroizer/models/payroll/timesheet_earnings_line.rb b/lib/xeroizer/models/payroll/timesheet_earnings_line.rb index 594fb690..cab521f7 100644 --- a/lib/xeroizer/models/payroll/timesheet_earnings_line.rb +++ b/lib/xeroizer/models/payroll/timesheet_earnings_line.rb @@ -1,11 +1,11 @@ module Xeroizer module Record module Payroll - + class TimesheetEarningsLineModel < PayrollBaseModel - + end - + # child of Payslip class TimesheetEarningsLine < PayrollBase @@ -15,6 +15,6 @@ class TimesheetEarningsLine < PayrollBase end - end + end end end diff --git a/lib/xeroizer/models/payroll/timesheet_line.rb b/lib/xeroizer/models/payroll/timesheet_line.rb index e68d9d68..e6fbdeaa 100644 --- a/lib/xeroizer/models/payroll/timesheet_line.rb +++ b/lib/xeroizer/models/payroll/timesheet_line.rb @@ -4,6 +4,8 @@ module Payroll class TimesheetLineModel < PayrollBaseModel + set_permissions :read, :write, :update # UK + end class TimesheetLine < PayrollBase @@ -20,9 +22,14 @@ class TimesheetLine < PayrollBase # UK datetime :date + guid :timesheet_id # used to make the URL datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC' + def api_url + "Timesheets/#{attributes[:timesheet_id]}/lines" + end + end end diff --git a/lib/xeroizer/record/base.rb b/lib/xeroizer/record/base.rb index c24f75cb..d6f6d568 100644 --- a/lib/xeroizer/record/base.rb +++ b/lib/xeroizer/record/base.rb @@ -23,6 +23,7 @@ class Base attr_writer :api_method_for_updating attr_accessor :paged_record_downloaded + attr_accessor :after_initialize include ModelDefinitionHelper include RecordAssociationHelper @@ -191,12 +192,21 @@ def update end def extra_params_for_create_or_update - json? ? {raw_body: true, content_type: "application/json"} : {} + json? ? {raw_body: true, content_type: "application/json", url: api_url} : {} end + def api_url; end # individual models can override this + def to_api_json attrs = self.attributes.reject {|k, v| k == :parent }.map do |k, v| - [k.to_s.camelize(:lower), v.kind_of?(Array) ? v.map(&:to_h) : (v.respond_to?(:to_api_json) ? v.to_api_json : v)] + value = if v.respond_to?(:to_api_json) + v.to_api_json + elsif v.is_a?(Array) && [0, 1].include?(v.count) + v.first # hack for timesheet line has_array values + else + v + end + [k.to_s.camelize(:lower), value] end Hash[attrs].to_json end From c425015f5bca84978c7ec835175a3ab0bf10292b Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Wed, 4 Oct 2017 14:46:19 +1000 Subject: [PATCH 54/82] fix bug when retrieving number of units on timesheet line --- lib/xeroizer/record/record_association_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/xeroizer/record/record_association_helper.rb b/lib/xeroizer/record/record_association_helper.rb index 872f333a..002067b4 100644 --- a/lib/xeroizer/record/record_association_helper.rb +++ b/lib/xeroizer/record/record_association_helper.rb @@ -168,7 +168,7 @@ def define_association_attribute(field_name, internal_field_name, association_ty when NilClass self.attributes[field_name] = [] - when Float + when Float, Fixnum if record_class.fields.count == 1 && record_class.fields.keys.first == :value self.attributes[field_name] = self.class.value_if_nil(association_type, value) else From 209e4aee96d55471fec0ff603b274014430b1f16 Mon Sep 17 00:00:00 2001 From: adamlyons2 Date: Tue, 14 Nov 2017 11:53:16 +1000 Subject: [PATCH 55/82] FIX: include attribute for reportable as w1 --- lib/xeroizer/models/payroll/earnings_rate.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/xeroizer/models/payroll/earnings_rate.rb b/lib/xeroizer/models/payroll/earnings_rate.rb index 8cd76ad2..9bea09bb 100644 --- a/lib/xeroizer/models/payroll/earnings_rate.rb +++ b/lib/xeroizer/models/payroll/earnings_rate.rb @@ -23,6 +23,7 @@ class EarningsRate < PayrollBase decimal :multiplier boolean :accrue_leave decimal :amount + boolean :is_reportable_as_w1 datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC' From 6fc608ee175a1c9969a484c640a79e822e1d727b Mon Sep 17 00:00:00 2001 From: adamlyons2 Date: Tue, 14 Nov 2017 12:40:33 +1000 Subject: [PATCH 56/82] ADD: reportable as w1 to earnings rate stubs --- test/stub_responses/payroll_pay_items.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/stub_responses/payroll_pay_items.xml b/test/stub_responses/payroll_pay_items.xml index cd80c0fb..0105607c 100644 --- a/test/stub_responses/payroll_pay_items.xml +++ b/test/stub_responses/payroll_pay_items.xml @@ -14,6 +14,7 @@ Hours false false + true 2013-04-09T23:45:25 @@ -25,6 +26,7 @@ 1.5000 false true + true false 2013-04-09T23:45:25 @@ -37,6 +39,7 @@ Fixed Amount false false + true 2013-04-09T23:45:25 @@ -48,6 +51,7 @@ Fixed Amount true true + true 2013-04-09T23:45:25 From 80d73493112d08497a43ceafeeeb45bc0a2fb5a5 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Thu, 15 Mar 2018 12:48:49 +1000 Subject: [PATCH 57/82] add a method for timesheeet approval - https://developer.xero.com/documentation/payroll-api-uk/timesheets --- lib/xeroizer/models/payroll/timesheet.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/xeroizer/models/payroll/timesheet.rb b/lib/xeroizer/models/payroll/timesheet.rb index 65211da6..2c9f103a 100644 --- a/lib/xeroizer/models/payroll/timesheet.rb +++ b/lib/xeroizer/models/payroll/timesheet.rb @@ -26,6 +26,13 @@ class Timesheet < PayrollBase validates_presence_of :start_date, :end_date, :employee_id + def approve + params = extra_params_for_create_or_update + params[:url] = "Timesheets/#{attributes[:timesheet_id]}/approve" + response = parent.send(:http_post, {}, params) + parse_save_response(response) + end + end end From 904977e1a7f59c0ccf31bada2e91d03e2e9849f8 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Mon, 26 Mar 2018 14:07:27 +1000 Subject: [PATCH 58/82] support UK and NZ leave balances --- lib/xeroizer/models/payroll/employee.rb | 4 ++-- lib/xeroizer/models/payroll/leave_balance.rb | 19 +++++++++++++++---- lib/xeroizer/payroll_application.rb | 1 + lib/xeroizer/record/base_model_http_proxy.rb | 1 + 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/xeroizer/models/payroll/employee.rb b/lib/xeroizer/models/payroll/employee.rb index 1c978ea0..5a16ce5f 100644 --- a/lib/xeroizer/models/payroll/employee.rb +++ b/lib/xeroizer/models/payroll/employee.rb @@ -43,8 +43,8 @@ class Employee < PayrollBase belongs_to :pay_template, :internal_name_singular => "pay_template", :model_name => "PayTemplate" belongs_to :opening_balances, :internal_name_singular => "opening_balance", :model_name => "OpeningBalances" has_many :super_memberships, :internal_name_singular => "super_membership", :model_name => "SuperMembership" - has_many :leave_balances, :internal_name_singular => "leave_balance", model_name: "LeaveBalance" # https://developer.xero.com/documentation/payroll-api/leavebalances/ - has_many :time_off_balances, :internal_name_singular => "time_off_balance", model_name: "TimeOffBalance" # https://developer.xero.com/documentation/payroll-api-us/timeoff-balances/ + has_many :leave_balances, :internal_name_singular => "leave_balance", model_name: "LeaveBalance" + has_many :time_off_balances, :internal_name_singular => "time_off_balance", model_name: "TimeOffBalance" end diff --git a/lib/xeroizer/models/payroll/leave_balance.rb b/lib/xeroizer/models/payroll/leave_balance.rb index 33ed389c..3b5184ad 100644 --- a/lib/xeroizer/models/payroll/leave_balance.rb +++ b/lib/xeroizer/models/payroll/leave_balance.rb @@ -1,21 +1,32 @@ module Xeroizer module Record module Payroll - + class LeaveBalanceModel < PayrollBaseModel - + + set_permissions :read, :write, :update + + def api_url(options = {}) + "employees/#{options.delete(:employee_id)}/leavebalances" + end end - + # child of Employee class LeaveBalance < PayrollBase + # AU: # https://developer.xero.com/documentation/payroll-api/leavebalances/ string :leave_name guid :leave_type_id decimal :number_of_units string :type_of_units + # UK: https://developer.xero.com/documentation/payroll-api-uk/employeeleavebalances + # NZ: https://developer.xero.com/documentation/payroll-api-nz/leavebalances + string :name + decimal :balance + end - end + end end end diff --git a/lib/xeroizer/payroll_application.rb b/lib/xeroizer/payroll_application.rb index bb156a32..70a1dd2f 100644 --- a/lib/xeroizer/payroll_application.rb +++ b/lib/xeroizer/payroll_application.rb @@ -34,6 +34,7 @@ def self.record(record_type) record :Setting record :SuperFund record :EarningsRate # UK + record :LeaveBalance # UK, NZ def initialize(application) @application = application diff --git a/lib/xeroizer/record/base_model_http_proxy.rb b/lib/xeroizer/record/base_model_http_proxy.rb index bd0199ac..52079517 100644 --- a/lib/xeroizer/record/base_model_http_proxy.rb +++ b/lib/xeroizer/record/base_model_http_proxy.rb @@ -29,6 +29,7 @@ def parse_params(options) params[:offset] = options[:offset] if options[:offset] params[:page] = options[:page] if options[:page] params[:response] = options[:api_format] || @application.api_format + params[:url] = self.send(:api_url, options) if self.respond_to?(:api_url) params end From 8852a06c6651020fae1b9b7358ee37dcef620f45 Mon Sep 17 00:00:00 2001 From: Juan Carlo Amican Date: Tue, 17 Jul 2018 12:44:42 +0800 Subject: [PATCH 59/82] ADD: Xero UK support for earningRates --- lib/xeroizer/models/payroll/earnings_rate.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/xeroizer/models/payroll/earnings_rate.rb b/lib/xeroizer/models/payroll/earnings_rate.rb index 9bea09bb..19199b21 100644 --- a/lib/xeroizer/models/payroll/earnings_rate.rb +++ b/lib/xeroizer/models/payroll/earnings_rate.rb @@ -23,6 +23,10 @@ class EarningsRate < PayrollBase decimal :multiplier boolean :accrue_leave decimal :amount + decimal :fixed_amount # UK + decimal :multiple_of_ordinary_earnings_rate # UK + string :expense_account_id # UK + boolean :current_record # UK boolean :is_reportable_as_w1 datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC' From 182f598959fe99c4a8fe02b61c46527c813e61c5 Mon Sep 17 00:00:00 2001 From: Juan Carlo Amican Date: Wed, 18 Jul 2018 11:08:34 +0800 Subject: [PATCH 60/82] ADD: Expose LeaveType API for Xero UK --- lib/xeroizer/models/payroll/leave_type.rb | 8 ++++---- lib/xeroizer/payroll_application.rb | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/xeroizer/models/payroll/leave_type.rb b/lib/xeroizer/models/payroll/leave_type.rb index 84f065be..bd3c42fc 100644 --- a/lib/xeroizer/models/payroll/leave_type.rb +++ b/lib/xeroizer/models/payroll/leave_type.rb @@ -1,11 +1,11 @@ module Xeroizer module Record module Payroll - + class LeaveTypeModel < PayrollBaseModel - + set_permissions :read, :write, :update end - + class LeaveType < PayrollBase string :name @@ -23,6 +23,6 @@ class LeaveType < PayrollBase end - end + end end end diff --git a/lib/xeroizer/payroll_application.rb b/lib/xeroizer/payroll_application.rb index 70a1dd2f..568f7892 100644 --- a/lib/xeroizer/payroll_application.rb +++ b/lib/xeroizer/payroll_application.rb @@ -34,6 +34,7 @@ def self.record(record_type) record :Setting record :SuperFund record :EarningsRate # UK + record :LeaveType # UK record :LeaveBalance # UK, NZ def initialize(application) From c3448fe9187f806808c176fffd883710941b242f Mon Sep 17 00:00:00 2001 From: Juan Carlo Amican Date: Fri, 20 Jul 2018 14:14:17 +0800 Subject: [PATCH 61/82] ADD: POST LeaveRequest support for Xero UK --- lib/xeroizer.rb | 2 +- lib/xeroizer/models/payroll/employee.rb | 2 ++ lib/xeroizer/models/payroll/leave.rb | 37 +++++++++++++++++++++ lib/xeroizer/models/payroll/leave_period.rb | 13 +++++--- lib/xeroizer/models/payroll/leave_type.rb | 6 ++++ lib/xeroizer/models/payroll/period.rb | 23 +++++++++++++ lib/xeroizer/payroll_application.rb | 2 ++ 7 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 lib/xeroizer/models/payroll/leave.rb create mode 100644 lib/xeroizer/models/payroll/period.rb diff --git a/lib/xeroizer.rb b/lib/xeroizer.rb index 115fee7b..e4258645 100644 --- a/lib/xeroizer.rb +++ b/lib/xeroizer.rb @@ -40,7 +40,7 @@ # Include payroll models ['home_address', 'bank_account', 'employee', 'timesheet', 'timesheet_line', 'number_of_unit', - 'leave_application', 'leave_period', 'pay_items', 'deduction_type', 'earnings_rate', + 'leave_application', 'leave', 'leave_period', 'period', 'pay_items', 'deduction_type', 'earnings_rate', 'reimbursement_type', 'leave_type', 'payroll_calendar', 'pay_template', 'super_membership', 'leave_line', 'reimbursement_line', 'super_line', 'deduction_line', 'earnings_line', 'opening_balance', 'pay_run', 'settings', 'tracking_categories', 'employee_groups', 'timesheet_categories', 'account', diff --git a/lib/xeroizer/models/payroll/employee.rb b/lib/xeroizer/models/payroll/employee.rb index 5a16ce5f..e8a701d9 100644 --- a/lib/xeroizer/models/payroll/employee.rb +++ b/lib/xeroizer/models/payroll/employee.rb @@ -44,6 +44,8 @@ class Employee < PayrollBase belongs_to :opening_balances, :internal_name_singular => "opening_balance", :model_name => "OpeningBalances" has_many :super_memberships, :internal_name_singular => "super_membership", :model_name => "SuperMembership" has_many :leave_balances, :internal_name_singular => "leave_balance", model_name: "LeaveBalance" + has_many :leave_types, :internal_name_singular => "leave_type", model_name: "LeaveType" + has_many :leaves, :internal_name_singular => "leave", model_name: "Leave" # UK has_many :time_off_balances, :internal_name_singular => "time_off_balance", model_name: "TimeOffBalance" end diff --git a/lib/xeroizer/models/payroll/leave.rb b/lib/xeroizer/models/payroll/leave.rb new file mode 100644 index 00000000..89f90804 --- /dev/null +++ b/lib/xeroizer/models/payroll/leave.rb @@ -0,0 +1,37 @@ +module Xeroizer + module Record + module Payroll + + class LeaveModel < PayrollBaseModel + + set_permissions :read, :write, :update + + def api_url + "employees/#{options[:employee_id]}/leave" + end + end + + class Leave < PayrollBase + + set_primary_key :leave_id + + guid :employee_id + guid :leave_id + guid :leave_type_id + string :description + date :start_date + date :end_date + datetime_utc :updated_date_utc + + + has_many :periods#, :internal_name_singular => "period", :model_name => "LeavePeriod" + + def api_url + "employees/#{employee_id}/leave" + end + + end + + end + end +end diff --git a/lib/xeroizer/models/payroll/leave_period.rb b/lib/xeroizer/models/payroll/leave_period.rb index beb40b26..136c086c 100644 --- a/lib/xeroizer/models/payroll/leave_period.rb +++ b/lib/xeroizer/models/payroll/leave_period.rb @@ -1,20 +1,23 @@ module Xeroizer module Record module Payroll - + class LeavePeriodModel < PayrollBaseModel - + end - + class LeavePeriod < PayrollBase - + decimal :number_of_units date :pay_period_start_date date :pay_period_end_date + date :period_start_date + date :period_end_date string :leave_period_status + string :period_status end - end + end end end diff --git a/lib/xeroizer/models/payroll/leave_type.rb b/lib/xeroizer/models/payroll/leave_type.rb index bd3c42fc..bdfb0c75 100644 --- a/lib/xeroizer/models/payroll/leave_type.rb +++ b/lib/xeroizer/models/payroll/leave_type.rb @@ -4,6 +4,12 @@ module Payroll class LeaveTypeModel < PayrollBaseModel set_permissions :read, :write, :update + + def api_url(options) + if options.keys.include?(:employee_id) + "employees/#{options.delete(:employee_id)}/leaveTypes" + end + end end class LeaveType < PayrollBase diff --git a/lib/xeroizer/models/payroll/period.rb b/lib/xeroizer/models/payroll/period.rb new file mode 100644 index 00000000..1585f48c --- /dev/null +++ b/lib/xeroizer/models/payroll/period.rb @@ -0,0 +1,23 @@ +module Xeroizer + module Record + module Payroll + + class PeriodModel < PayrollBaseModel + + end + + class Period < PayrollBase + + decimal :number_of_units + date :pay_period_start_date + date :pay_period_end_date + date :period_start_date + date :period_end_date + string :leave_period_status + string :period_status + + end + + end + end +end diff --git a/lib/xeroizer/payroll_application.rb b/lib/xeroizer/payroll_application.rb index 568f7892..fa7d6c7c 100644 --- a/lib/xeroizer/payroll_application.rb +++ b/lib/xeroizer/payroll_application.rb @@ -34,6 +34,8 @@ def self.record(record_type) record :Setting record :SuperFund record :EarningsRate # UK + record :Leave # UK + record :Period record :LeaveType # UK record :LeaveBalance # UK, NZ From 2b7c0c7e718813196c3b4da12d85d88b2a5f29f1 Mon Sep 17 00:00:00 2001 From: Juan Carlo Amican Date: Tue, 24 Jul 2018 11:10:08 +0800 Subject: [PATCH 62/82] ADD: Support TimesheetLine with TrackingCategoryID for Xero UK --- .../models/payroll/tracking_categories.rb | 19 ++++++++++++++----- lib/xeroizer/payroll_application.rb | 3 ++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/xeroizer/models/payroll/tracking_categories.rb b/lib/xeroizer/models/payroll/tracking_categories.rb index ff3a632a..fbaa9a22 100644 --- a/lib/xeroizer/models/payroll/tracking_categories.rb +++ b/lib/xeroizer/models/payroll/tracking_categories.rb @@ -1,26 +1,35 @@ module Xeroizer module Record module Payroll - + class TrackingCategoriesModel < PayrollBaseModel set_permissions :read - + set_standalone_model true set_xml_root_name 'TrackingCategories' set_xml_node_name 'TrackingCategories' + + def api_url(options = {}) + if options.has_key?(:use_json_api) && options[:use_json_api] + "settings/trackingCategories" + end + end end - + # child of Settings class TrackingCategories < PayrollBase set_primary_key false - + + guid :employee_groups_tracking_category_id + guid :timesheet_tracking_category_id + has_many :employee_groups has_many :timesheet_categories end - end + end end end diff --git a/lib/xeroizer/payroll_application.rb b/lib/xeroizer/payroll_application.rb index fa7d6c7c..59b20cd6 100644 --- a/lib/xeroizer/payroll_application.rb +++ b/lib/xeroizer/payroll_application.rb @@ -35,9 +35,10 @@ def self.record(record_type) record :SuperFund record :EarningsRate # UK record :Leave # UK - record :Period + record :Period # UK record :LeaveType # UK record :LeaveBalance # UK, NZ + record :TrackingCategories # UK def initialize(application) @application = application From da40e72c51e212263fee60f67a7283c71249df28 Mon Sep 17 00:00:00 2001 From: adamlyons2 Date: Wed, 25 Jul 2018 14:53:58 +1000 Subject: [PATCH 63/82] ADD: pay item stub for employment termination payment type --- test/stub_responses/payroll_pay_items.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/stub_responses/payroll_pay_items.xml b/test/stub_responses/payroll_pay_items.xml index 0105607c..c8b35c88 100644 --- a/test/stub_responses/payroll_pay_items.xml +++ b/test/stub_responses/payroll_pay_items.xml @@ -54,6 +54,18 @@ true 2013-04-09T23:45:25 + + 683f87af-50f7-4584-9b0b-93d943537945 + ETP Type O Payments Subject To Tax + EMPLOYMENTTERMINATIONPAYMENT + FIXEDAMOUNT + 477 + false + true + false + 2018-02-07T01:01:25 + O + From 29c32a4a763878cf2f22369301d5df28ea6139f5 Mon Sep 17 00:00:00 2001 From: adamlyons2 Date: Wed, 25 Jul 2018 14:54:15 +1000 Subject: [PATCH 64/82] FIX: include param for etp type --- lib/xeroizer/models/payroll/earnings_rate.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/xeroizer/models/payroll/earnings_rate.rb b/lib/xeroizer/models/payroll/earnings_rate.rb index 9bea09bb..813302b2 100644 --- a/lib/xeroizer/models/payroll/earnings_rate.rb +++ b/lib/xeroizer/models/payroll/earnings_rate.rb @@ -24,6 +24,7 @@ class EarningsRate < PayrollBase boolean :accrue_leave decimal :amount boolean :is_reportable_as_w1 + string :employment_termination_payment_type datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC' From f30265567af2c6e62199ba0b8f41d5c0e09454e0 Mon Sep 17 00:00:00 2001 From: Juan Carlo Amican Date: Sun, 29 Jul 2018 20:45:07 +0800 Subject: [PATCH 65/82] UPDATE: Open Leave Period Endpoint for Xero UK. --- lib/xeroizer/models/payroll/employee.rb | 1 - lib/xeroizer/models/payroll/leave.rb | 4 +--- lib/xeroizer/models/payroll/leave_period.rb | 3 --- lib/xeroizer/models/payroll/period.rb | 10 +++++++--- lib/xeroizer/record/base.rb | 4 ++-- 5 files changed, 10 insertions(+), 12 deletions(-) diff --git a/lib/xeroizer/models/payroll/employee.rb b/lib/xeroizer/models/payroll/employee.rb index e8a701d9..8ea7b9b6 100644 --- a/lib/xeroizer/models/payroll/employee.rb +++ b/lib/xeroizer/models/payroll/employee.rb @@ -45,7 +45,6 @@ class Employee < PayrollBase has_many :super_memberships, :internal_name_singular => "super_membership", :model_name => "SuperMembership" has_many :leave_balances, :internal_name_singular => "leave_balance", model_name: "LeaveBalance" has_many :leave_types, :internal_name_singular => "leave_type", model_name: "LeaveType" - has_many :leaves, :internal_name_singular => "leave", model_name: "Leave" # UK has_many :time_off_balances, :internal_name_singular => "time_off_balance", model_name: "TimeOffBalance" end diff --git a/lib/xeroizer/models/payroll/leave.rb b/lib/xeroizer/models/payroll/leave.rb index 89f90804..9e9eb97d 100644 --- a/lib/xeroizer/models/payroll/leave.rb +++ b/lib/xeroizer/models/payroll/leave.rb @@ -23,13 +23,11 @@ class Leave < PayrollBase date :end_date datetime_utc :updated_date_utc - - has_many :periods#, :internal_name_singular => "period", :model_name => "LeavePeriod" + has_many :periods def api_url "employees/#{employee_id}/leave" end - end end diff --git a/lib/xeroizer/models/payroll/leave_period.rb b/lib/xeroizer/models/payroll/leave_period.rb index 136c086c..c0a8d93e 100644 --- a/lib/xeroizer/models/payroll/leave_period.rb +++ b/lib/xeroizer/models/payroll/leave_period.rb @@ -11,10 +11,7 @@ class LeavePeriod < PayrollBase decimal :number_of_units date :pay_period_start_date date :pay_period_end_date - date :period_start_date - date :period_end_date string :leave_period_status - string :period_status end diff --git a/lib/xeroizer/models/payroll/period.rb b/lib/xeroizer/models/payroll/period.rb index 1585f48c..b17c9b01 100644 --- a/lib/xeroizer/models/payroll/period.rb +++ b/lib/xeroizer/models/payroll/period.rb @@ -3,19 +3,23 @@ module Record module Payroll class PeriodModel < PayrollBaseModel + set_permissions :read + def api_url(options) + "employees/#{options.delete(:employee_id)}/leavePeriods?startDate=#{options[:start_date]}&endDate=#{options[:end_date]}" + end end class Period < PayrollBase decimal :number_of_units - date :pay_period_start_date - date :pay_period_end_date date :period_start_date date :period_end_date - string :leave_period_status string :period_status + def to_api_json + JSON.parse(super) + end end end diff --git a/lib/xeroizer/record/base.rb b/lib/xeroizer/record/base.rb index d6f6d568..6226c5a7 100644 --- a/lib/xeroizer/record/base.rb +++ b/lib/xeroizer/record/base.rb @@ -168,7 +168,6 @@ def create log "[CREATE SENT] (#{__FILE__}:#{__LINE__}) #{request}" response = parent.send(api_method_for_creating, request, extra_params_for_create_or_update) - log "[CREATE RECEIVED] (#{__FILE__}:#{__LINE__}) #{response}" parse_save_response(response) @@ -181,7 +180,6 @@ def update end request = json? ? to_api_json : to_xml - log "[UPDATE SENT] (#{__FILE__}:#{__LINE__}) \r\n#{request}" response = parent.send(api_method_for_updating, request, extra_params_for_create_or_update) @@ -201,6 +199,8 @@ def to_api_json attrs = self.attributes.reject {|k, v| k == :parent }.map do |k, v| value = if v.respond_to?(:to_api_json) v.to_api_json + elsif k == :periods # hack for leave request periods for xero uk + v.map(&:to_api_json) elsif v.is_a?(Array) && [0, 1].include?(v.count) v.first # hack for timesheet line has_array values else From 79657eb1b53b454f51ce43ad1ebdb0683b8d6392 Mon Sep 17 00:00:00 2001 From: Juan Carlo Amican Date: Tue, 31 Jul 2018 12:20:48 +0800 Subject: [PATCH 66/82] ADD: Support for Xero UK POST EarningsRate --- lib/xeroizer/models/payroll/earnings_rate.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/xeroizer/models/payroll/earnings_rate.rb b/lib/xeroizer/models/payroll/earnings_rate.rb index 0b87db56..cec7e88b 100644 --- a/lib/xeroizer/models/payroll/earnings_rate.rb +++ b/lib/xeroizer/models/payroll/earnings_rate.rb @@ -32,7 +32,7 @@ class EarningsRate < PayrollBase datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC' - validates_presence_of :name, :account_code, :type_of_units, :is_exempt_from_super, :is_exempt_from_tax, :earnings_type + validates_presence_of :name, :type_of_units, :earnings_type # UK has lesser validation requirements end From ebb0a1a7dd057001b3696184aa5c106ab0722782 Mon Sep 17 00:00:00 2001 From: Juan Carlo Amican Date: Thu, 2 Aug 2018 14:35:57 +0800 Subject: [PATCH 67/82] UPDATE: Remove some EarningsRate Validation for Xero UK --- lib/xeroizer/models/payroll/earnings_rate.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/xeroizer/models/payroll/earnings_rate.rb b/lib/xeroizer/models/payroll/earnings_rate.rb index cec7e88b..8207c9d9 100644 --- a/lib/xeroizer/models/payroll/earnings_rate.rb +++ b/lib/xeroizer/models/payroll/earnings_rate.rb @@ -32,7 +32,7 @@ class EarningsRate < PayrollBase datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC' - validates_presence_of :name, :type_of_units, :earnings_type # UK has lesser validation requirements + validates_presence_of :name, :earnings_type # UK has lesser validation requirements end From ca2e41581164b44cb07c7395627e76362923d6fa Mon Sep 17 00:00:00 2001 From: Juan Carlo Amican Date: Fri, 3 Aug 2018 12:21:38 +0800 Subject: [PATCH 68/82] UPDATE: Leave api_url not working on fetch with employee_id --- lib/xeroizer/models/payroll/leave.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/xeroizer/models/payroll/leave.rb b/lib/xeroizer/models/payroll/leave.rb index 9e9eb97d..f0f1a65e 100644 --- a/lib/xeroizer/models/payroll/leave.rb +++ b/lib/xeroizer/models/payroll/leave.rb @@ -6,7 +6,7 @@ class LeaveModel < PayrollBaseModel set_permissions :read, :write, :update - def api_url + def api_url(options = {}) "employees/#{options[:employee_id]}/leave" end end From 8fadc9baad70dd8ac283db800ec3aec9c9d0fa9a Mon Sep 17 00:00:00 2001 From: Juan Carlo Amican Date: Tue, 7 Aug 2018 13:58:30 +0800 Subject: [PATCH 69/82] ADD: EmployeeLeaveType Endpoints --- lib/xeroizer.rb | 2 +- .../models/payroll/employee_leave_type.rb | 45 +++++++++++++++++++ lib/xeroizer/models/payroll/leave_type.rb | 6 --- lib/xeroizer/payroll_application.rb | 2 + lib/xeroizer/record/base_model.rb | 9 +++- 5 files changed, 56 insertions(+), 8 deletions(-) create mode 100644 lib/xeroizer/models/payroll/employee_leave_type.rb diff --git a/lib/xeroizer.rb b/lib/xeroizer.rb index e4258645..1050c3ef 100644 --- a/lib/xeroizer.rb +++ b/lib/xeroizer.rb @@ -39,7 +39,7 @@ end # Include payroll models -['home_address', 'bank_account', 'employee', 'timesheet', 'timesheet_line', 'number_of_unit', +['home_address', 'bank_account', 'employee', 'employee_leave_type', 'timesheet', 'timesheet_line', 'number_of_unit', 'leave_application', 'leave', 'leave_period', 'period', 'pay_items', 'deduction_type', 'earnings_rate', 'reimbursement_type', 'leave_type', 'payroll_calendar', 'pay_template', 'super_membership', 'leave_line', 'reimbursement_line', 'super_line', 'deduction_line', 'earnings_line', 'opening_balance', diff --git a/lib/xeroizer/models/payroll/employee_leave_type.rb b/lib/xeroizer/models/payroll/employee_leave_type.rb new file mode 100644 index 00000000..1d27c51c --- /dev/null +++ b/lib/xeroizer/models/payroll/employee_leave_type.rb @@ -0,0 +1,45 @@ +def api_url(options = {}) + if options[:employee_id] + "employees/#{options.delete(:employee_id)}/leaveTypes" + end +end + +module Xeroizer + module Record + module Payroll + + class EmployeeLeaveTypeModel < PayrollBaseModel + set_permissions :read, :write, :update + + def api_url(options) + if options.keys.include?(:employee_id) + "employees/#{options.delete(:employee_id)}/leaveTypes" + end + end + + def model_name_to_parse + # HACK so we can use LeaveType to be the iterable + "LeaveType" + end + end + + class EmployeeLeaveType < PayrollBase + guid :leave_type_id + guid :employee_id + + string :schedule_of_accrual + decimal :hours_accrued_annually + decimal :maximum_to_accrue + decimal :opening_balance + decimal :rate_accrued_hourly + + validates_presence_of :leave_type_id, :schedule_of_accrual + + def api_url + "employees/#{employee_id}/leaveTypes" + end + end + + end + end +end diff --git a/lib/xeroizer/models/payroll/leave_type.rb b/lib/xeroizer/models/payroll/leave_type.rb index bdfb0c75..bd3c42fc 100644 --- a/lib/xeroizer/models/payroll/leave_type.rb +++ b/lib/xeroizer/models/payroll/leave_type.rb @@ -4,12 +4,6 @@ module Payroll class LeaveTypeModel < PayrollBaseModel set_permissions :read, :write, :update - - def api_url(options) - if options.keys.include?(:employee_id) - "employees/#{options.delete(:employee_id)}/leaveTypes" - end - end end class LeaveType < PayrollBase diff --git a/lib/xeroizer/payroll_application.rb b/lib/xeroizer/payroll_application.rb index 59b20cd6..f09b503c 100644 --- a/lib/xeroizer/payroll_application.rb +++ b/lib/xeroizer/payroll_application.rb @@ -39,6 +39,8 @@ def self.record(record_type) record :LeaveType # UK record :LeaveBalance # UK, NZ record :TrackingCategories # UK + record :EmployeeLeaveType # UK + def initialize(application) @application = application diff --git a/lib/xeroizer/record/base_model.rb b/lib/xeroizer/record/base_model.rb index 2cdd2008..ce79e17d 100644 --- a/lib/xeroizer/record/base_model.rb +++ b/lib/xeroizer/record/base_model.rb @@ -218,7 +218,14 @@ def parse_json_response(response_body, options = {}) json = ::JSON.parse(response_body) response = Response.new - iterable = json[self.model_name.camelize(:lower).pluralize] || json[self.model_name.camelize(:lower)] + model_name_to_parse = + if self.respond_to?(:model_name_to_parse) + self.model_name_to_parse + else + self.model_name + end + + iterable = json[model_name_to_parse.camelize(:lower).pluralize] || json[model_name_to_parse.camelize(:lower)] iterable = [iterable] if iterable.is_a?(Hash) iterable.each {|object| From 116349e207fcd85a7d6102a3d6cc81db65f40c66 Mon Sep 17 00:00:00 2001 From: Juan Carlo Amican Date: Tue, 7 Aug 2018 14:12:27 +0800 Subject: [PATCH 70/82] REMOVE: dirty method --- lib/xeroizer/models/payroll/employee_leave_type.rb | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/xeroizer/models/payroll/employee_leave_type.rb b/lib/xeroizer/models/payroll/employee_leave_type.rb index 1d27c51c..f2146e03 100644 --- a/lib/xeroizer/models/payroll/employee_leave_type.rb +++ b/lib/xeroizer/models/payroll/employee_leave_type.rb @@ -1,9 +1,3 @@ -def api_url(options = {}) - if options[:employee_id] - "employees/#{options.delete(:employee_id)}/leaveTypes" - end -end - module Xeroizer module Record module Payroll From 64fc95a58ddf43b5947ad7bad5fcf4bbba77bcdf Mon Sep 17 00:00:00 2001 From: Daniel Gilchrist Date: Mon, 18 Feb 2019 09:32:31 +1000 Subject: [PATCH 71/82] Added EndDate field to payroll/employee model --- lib/xeroizer/models/payroll/employee.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/xeroizer/models/payroll/employee.rb b/lib/xeroizer/models/payroll/employee.rb index 8ea7b9b6..9922be93 100644 --- a/lib/xeroizer/models/payroll/employee.rb +++ b/lib/xeroizer/models/payroll/employee.rb @@ -35,7 +35,9 @@ class Employee < PayrollBase date :termination_date string :national_insurance_number # UK guid :pay_run_calendar_id # UK - datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC' + datetime_utc :updated_date_utc, api_name: 'UpdatedDateUTC' + date :end_date, api_name: 'EndDate' # UK - null when employee is active + belongs_to :home_address, :internal_name_singular => "home_address", :model_name => "HomeAddress" belongs_to :tax_declaration, :internal_name_singular => "tax_declaration", :model_name => "TaxDeclaration" From 5564a88a2a6e04f0a238f4ca45bfef292484f00c Mon Sep 17 00:00:00 2001 From: Daniel Gilchrist Date: Tue, 19 Feb 2019 15:45:37 +1000 Subject: [PATCH 72/82] Added end_date to Employee model --- lib/xeroizer/models/employee.rb | 17 +++++++++-------- lib/xeroizer/models/payroll/employee.rb | 1 - 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/xeroizer/models/employee.rb b/lib/xeroizer/models/employee.rb index 02743c6f..2568835c 100644 --- a/lib/xeroizer/models/employee.rb +++ b/lib/xeroizer/models/employee.rb @@ -2,17 +2,17 @@ module Xeroizer module Record - + class EmployeeModel < BaseModel - + set_permissions :read, :write, :update - + end - + class Employee < Base - + set_primary_key :employee_id - + guid :employee_id string :status string :first_name @@ -29,8 +29,9 @@ class Employee < Base boolean :is_authorised_to_approve_timesheets string :employee_group_name - datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC' - + datetime_utc :updated_date_utc, api_name: 'UpdatedDateUTC' + date :end_date, api_name: 'EndDate' # UK - null when employee is active + belongs_to :external_link validates_presence_of :first_name, :last_name, :date_of_birth diff --git a/lib/xeroizer/models/payroll/employee.rb b/lib/xeroizer/models/payroll/employee.rb index 9922be93..b26bd8be 100644 --- a/lib/xeroizer/models/payroll/employee.rb +++ b/lib/xeroizer/models/payroll/employee.rb @@ -38,7 +38,6 @@ class Employee < PayrollBase datetime_utc :updated_date_utc, api_name: 'UpdatedDateUTC' date :end_date, api_name: 'EndDate' # UK - null when employee is active - belongs_to :home_address, :internal_name_singular => "home_address", :model_name => "HomeAddress" belongs_to :tax_declaration, :internal_name_singular => "tax_declaration", :model_name => "TaxDeclaration" has_many :bank_accounts From ba47e4b3251ef52b221de73b3fc575926f021977 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Mon, 17 Jun 2019 14:58:11 -0500 Subject: [PATCH 73/82] Update BigDecimal due to dep warning --- lib/xeroizer/record/xml_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/xeroizer/record/xml_helper.rb b/lib/xeroizer/record/xml_helper.rb index 58552ab5..035facf1 100644 --- a/lib/xeroizer/record/xml_helper.rb +++ b/lib/xeroizer/record/xml_helper.rb @@ -23,7 +23,7 @@ def build_from_node(node, parent, base_module, standalone_model = false) when :string then element.text when :boolean then (element.text == 'true') when :integer then element.text.to_i - when :decimal then BigDecimal.new(element.text) + when :decimal then BigDecimal(element.text) when :date then Date.parse(element.text) when :datetime then Time.parse(element.text) when :datetime_utc then ActiveSupport::TimeZone['UTC'].parse(element.text).utc From e8c9f9b99194203a9016fbda567af730b3f1733f Mon Sep 17 00:00:00 2001 From: Ed Lebert Date: Mon, 25 Feb 2019 15:18:44 -0600 Subject: [PATCH 74/82] cherry pick https://github.com/waynerobinson/xeroizer/commit/accb69b62e0cd72a0e756fcf97bbba3a798774d5#diff-abcef2bf7c04d59766b55bdbdabdcc68 --- lib/xeroizer/models/credit_note.rb | 42 +++++++++---------- lib/xeroizer/models/invoice.rb | 8 ++-- lib/xeroizer/record/xml_helper.rb | 2 +- .../report/aged_receivables_by_contact.rb | 11 +++-- lib/xeroizer/report/cell_xml_helper.rb | 2 +- test/unit/models/repeating_invoice_test.rb | 2 +- 6 files changed, 33 insertions(+), 34 deletions(-) diff --git a/lib/xeroizer/models/credit_note.rb b/lib/xeroizer/models/credit_note.rb index fd9048c2..a6a3fdf1 100644 --- a/lib/xeroizer/models/credit_note.rb +++ b/lib/xeroizer/models/credit_note.rb @@ -1,10 +1,10 @@ module Xeroizer module Record - + class CreditNoteModel < BaseModel - + set_permissions :read, :write, :update - + public # Retrieve the PDF version of the credit matching the `id`. @@ -19,11 +19,11 @@ def pdf(id, filename = nil) pdf_data end end - + end - + class CreditNote < Base - + CREDIT_NOTE_STATUS = { 'AUTHORISED' => 'Approved credit_notes awaiting payment', 'DELETED' => 'Draft credit_notes that are deleted', @@ -33,17 +33,17 @@ class CreditNote < Base 'VOIDED' => 'Approved credit_notes that are voided' } unless defined?(CREDIT_NOTE_STATUS) CREDIT_NOTE_STATUSES = CREDIT_NOTE_STATUS.keys.sort - + CREDIT_NOTE_TYPE = { 'ACCRECCREDIT' => 'Accounts Receivable', 'ACCPAYCREDIT' => 'Accounts Payable' } unless defined?(CREDIT_NOTE_TYPE) CREDIT_NOTE_TYPES = CREDIT_NOTE_TYPE.keys.sort - + set_primary_key :credit_note_id set_possible_primary_keys :credit_note_id, :credit_note_number list_contains_summary_only true - + guid :credit_note_id string :credit_note_number string :reference @@ -65,15 +65,15 @@ class CreditNote < Base belongs_to :contact has_many :line_items has_many :allocations - + validates_inclusion_of :type, :in => CREDIT_NOTE_TYPES validates_inclusion_of :status, :in => CREDIT_NOTE_STATUSES, :allow_blanks => true validates_associated :contact validates_associated :line_items validates_associated :allocations, :allow_blanks => true - + public - + # Access the contact name without forcing a download of # an incomplete, summary credit note. def contact_name @@ -84,21 +84,21 @@ def contact_name # incomplete, summary credit note. def contact_id attributes[:contact] && attributes[:contact][:contact_id] - end - + end + # Swallow assignment of attributes that should only be calculated automatically. def sub_total=(value); raise SettingTotalDirectlyNotSupported.new(:sub_total); end def total_tax=(value); raise SettingTotalDirectlyNotSupported.new(:total_tax); end def total=(value); raise SettingTotalDirectlyNotSupported.new(:total); end - + # Calculate sub_total from line_items. def sub_total(always_summary = false) if !always_summary && (new_record? || (!new_record? && line_items && line_items.size > 0)) - sum = (line_items || []).inject(BigDecimal.new('0')) { | sum, line_item | sum + line_item.line_amount } - + overall_sum = (line_items || []).inject(BigDecimal('0')) { | sum, line_item | sum + line_item.line_amount } + # If the default amount types are inclusive of 'tax' then remove the tax amount from this sub-total. - sum -= total_tax if line_amount_types == 'Inclusive' - sum + overall_sum -= total_tax if line_amount_types == 'Inclusive' + overall_sum else attributes[:sub_total] end @@ -107,7 +107,7 @@ def sub_total(always_summary = false) # Calculate total_tax from line_items. def total_tax(always_summary = false) if !always_summary && (new_record? || (!new_record? && line_items && line_items.size > 0)) - (line_items || []).inject(BigDecimal.new('0')) { | sum, line_item | sum + line_item.tax_amount } + (line_items || []).inject(BigDecimal('0')) { | sum, line_item | sum + line_item.tax_amount } else attributes[:total_tax] end @@ -121,7 +121,7 @@ def total(always_summary = false) attributes[:total] end end - + # Retrieve the PDF version of this credit note. # @param [String] filename optional filename to store the PDF in instead of returning the data. def pdf(filename = nil) diff --git a/lib/xeroizer/models/invoice.rb b/lib/xeroizer/models/invoice.rb index 563ac83e..6075d199 100644 --- a/lib/xeroizer/models/invoice.rb +++ b/lib/xeroizer/models/invoice.rb @@ -140,11 +140,11 @@ def total=(total) # Calculate sub_total from line_items. def sub_total(always_summary = false) if !@sub_total_is_set && not_summary_or_loaded_record(always_summary) - sum = (line_items || []).inject(BigDecimal.new('0')) { | sum, line_item | sum + line_item.line_amount } + overall_sum = (line_items || []).inject(BigDecimal('0')) { | sum, line_item | sum + line_item.line_amount } # If the default amount types are inclusive of 'tax' then remove the tax amount from this sub-total. - sum -= total_tax if line_amount_types == 'Inclusive' - sum + overall_sum -= total_tax if line_amount_types == 'Inclusive' + overall_sum else attributes[:sub_total] end @@ -153,7 +153,7 @@ def sub_total(always_summary = false) # Calculate total_tax from line_items. def total_tax(always_summary = false) if !@total_tax_is_set && not_summary_or_loaded_record(always_summary) - (line_items || []).inject(BigDecimal.new('0')) { | sum, line_item | sum + line_item.tax_amount } + (line_items || []).inject(BigDecimal('0')) { | sum, line_item | sum + line_item.tax_amount } else attributes[:total_tax] end diff --git a/lib/xeroizer/record/xml_helper.rb b/lib/xeroizer/record/xml_helper.rb index 035facf1..38357b8e 100644 --- a/lib/xeroizer/record/xml_helper.rb +++ b/lib/xeroizer/record/xml_helper.rb @@ -143,7 +143,7 @@ def xml_value_from_field(b, field, value) when :decimal real_value = case value when BigDecimal then value.to_s - when String then BigDecimal.new(value).to_s + when String then BigDecimal(value).to_s else value end b.tag!(field[:api_name], real_value) diff --git a/lib/xeroizer/report/aged_receivables_by_contact.rb b/lib/xeroizer/report/aged_receivables_by_contact.rb index f373c732..37aa8f59 100644 --- a/lib/xeroizer/report/aged_receivables_by_contact.rb +++ b/lib/xeroizer/report/aged_receivables_by_contact.rb @@ -5,7 +5,7 @@ class AgedReceivablesByContact < Base public def total - @_total_cache ||= summary.cell(:Total).value + @_total_cache ||= summary.cell(:Total).value end def total_paid @@ -22,22 +22,21 @@ def total_due def total_overdue return @_total_due_cache if @_total_due_cache - + now = Time.now - @_total_due_cache = sum(:Due) do | row | + @_total_due_cache = sum(:Due) do | row | due_date = row.cell('Due Date').value due_date && due_date < now end end def sum(column_name, &block) - sections.first.rows.inject(BigDecimal.new('0')) do | sum, row | - cell = row.cell(column_name) + sections.first.rows.inject(BigDecimal('0')) do | sum, row | sum += row.cell(column_name).value if row.class == Xeroizer::Report::Row && (block.nil? || block.call(row)) sum end end - + end end end diff --git a/lib/xeroizer/report/cell_xml_helper.rb b/lib/xeroizer/report/cell_xml_helper.rb index 7294868e..f5455488 100644 --- a/lib/xeroizer/report/cell_xml_helper.rb +++ b/lib/xeroizer/report/cell_xml_helper.rb @@ -47,7 +47,7 @@ def build_from_node(node) def parse_value(value) case value - when /^[-]?\d+(\.\d+)?$/ then BigDecimal.new(value) + when /^[-]?\d+(\.\d+)?$/ then BigDecimal(value) when /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$/ then Time.xmlschema(value) else value end diff --git a/test/unit/models/repeating_invoice_test.rb b/test/unit/models/repeating_invoice_test.rb index 8ad19df6..d5eeaa7a 100644 --- a/test/unit/models/repeating_invoice_test.rb +++ b/test/unit/models/repeating_invoice_test.rb @@ -18,7 +18,7 @@ def setup repeating_invoice = repeating_invoices.first assert_equal "PowerDirect", repeating_invoice.contact_name - assert_equal BigDecimal.new(90), repeating_invoice.total + assert_equal BigDecimal(90), repeating_invoice.total assert_equal true, repeating_invoice.accounts_payable? schedule = repeating_invoice.schedule From f2e333f3a0d79d3b837ae28cdd3bab920bec4c05 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Wed, 19 Jun 2019 09:23:16 -0500 Subject: [PATCH 75/82] missed one more BigDecimal update --- lib/xeroizer/record/xml_helper.rb | 34 +++++++++++++++---------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/lib/xeroizer/record/xml_helper.rb b/lib/xeroizer/record/xml_helper.rb index 38357b8e..a9fa230f 100644 --- a/lib/xeroizer/record/xml_helper.rb +++ b/lib/xeroizer/record/xml_helper.rb @@ -3,14 +3,14 @@ module Xeroizer module Record module XmlHelper - + def self.included(base) base.extend(ClassMethods) base.send :include, InstanceMethods end - + module ClassMethods - + # Build a record instance from the XML node. def build_from_node(node, parent, base_module, standalone_model = false) record = new(parent) @@ -27,10 +27,10 @@ def build_from_node(node, parent, base_module, standalone_model = false) when :date then Date.parse(element.text) when :datetime then Time.parse(element.text) when :datetime_utc then ActiveSupport::TimeZone['UTC'].parse(element.text).utc - when :belongs_to + when :belongs_to model_name = field[:model_name] ? field[:model_name].to_sym : element.name.to_sym base_module.const_get(model_name).build_from_node(element, parent, base_module) - + when :has_many if element.element_children.size > 0 sub_field_name = field[:model_name] ? field[:model_name].to_sym : (standalone_model ? element.name : element.children.first.name).to_sym @@ -78,7 +78,7 @@ def build_from_node(node, parent, base_module, standalone_model = false) when :string then element.text when :boolean then (element.text == 'true') when :integer then element.text.to_i - when :decimal then BigDecimal.new(element.text) + when :decimal then BigDecimal(element.text) when :date then Date.parse(element.text) when :datetime then Time.parse(element.text) when :datetime_utc then ActiveSupport::TimeZone['UTC'].parse(element.text).utc @@ -96,13 +96,13 @@ def build_from_node(node, parent, base_module, standalone_model = false) def remove_empty_text_nodes(children) children.find_all {|c| !c.respond_to?(:text) || !c.text.strip.empty?} end - + end - + module InstanceMethods - + public - + # Turn a record into its XML representation. def to_xml(b = Builder::XmlMarkup.new(:indent => 2)) optional_root_tag(parent.class.optional_xml_root_name, b) do |b| @@ -115,9 +115,9 @@ def to_xml(b = Builder::XmlMarkup.new(:indent => 2)) } end end - + protected - + # Add top-level root name if required. # E.g. Payments need specifying in the form: # @@ -132,7 +132,7 @@ def optional_root_tag(root_name, b, &block) yield(b) end end - + # Format an attribute for use in the XML passed to Xero. def xml_value_from_field(b, field, value) case field[:type] @@ -140,7 +140,7 @@ def xml_value_from_field(b, field, value) when :string then b.tag!(field[:api_name], value) when :boolean then b.tag!(field[:api_name], value ? 'true' : 'false') when :integer then b.tag!(field[:api_name], value.to_i) - when :decimal + when :decimal real_value = case value when BigDecimal then value.to_s when String then BigDecimal(value).to_s @@ -154,9 +154,9 @@ def xml_value_from_field(b, field, value) when Time then value.utc.strftime("%Y-%m-%d") end b.tag!(field[:api_name], real_value) - + when :datetime then b.tag!(field[:api_name], value.utc.strftime("%Y-%m-%dT%H:%M:%S")) - when :belongs_to + when :belongs_to value_is_present = (value.respond_to?(:blank?) && !value.blank?) || (!value.nil? && (!value.respond_to?(:length) || (value.respond_to?(:length) && value.length != 0))) # you may need to set a belongs_to to nil, at which point it defaults back to an array # but in that case we don't want to include it in the XML response @@ -167,7 +167,7 @@ def xml_value_from_field(b, field, value) nil end - when :has_many + when :has_many if value.size > 0 sub_parent = value.first.parent b.tag!(sub_parent.class.xml_root_name || sub_parent.model_name.pluralize) { From a299fe1fcc7bd1a4d78678d05fc086d052969418 Mon Sep 17 00:00:00 2001 From: Philip Fraser Date: Wed, 24 Jul 2019 12:13:51 +0200 Subject: [PATCH 76/82] Pay template url --- lib/xeroizer/models/payroll/pay_template.rb | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/xeroizer/models/payroll/pay_template.rb b/lib/xeroizer/models/payroll/pay_template.rb index ce70dc2d..2de96266 100644 --- a/lib/xeroizer/models/payroll/pay_template.rb +++ b/lib/xeroizer/models/payroll/pay_template.rb @@ -1,18 +1,22 @@ module Xeroizer module Record module Payroll - + class PayTemplateModel < PayrollBaseModel - + set_standalone_model true set_xml_root_name 'PayTemplate' set_xml_node_name 'PayTemplate' + + def api_url(options = {}) + "employees/#{options.delete(:employee_id)}/paytemplates" + end end - + class PayTemplate < PayrollBase set_primary_key false - + has_many :earnings_lines has_many :deduction_lines has_many :super_lines @@ -21,6 +25,6 @@ class PayTemplate < PayrollBase end - end + end end end From 4b5afed63793187f7944d5640b28857c493c12b1 Mon Sep 17 00:00:00 2001 From: Philip Fraser Date: Thu, 25 Jul 2019 10:56:47 +0200 Subject: [PATCH 77/82] Paytemplate and earnings template UK add permissions fix typo Earning template typo fixes Rename file revert earning template changes Earning template update xeroizer --- lib/xeroizer.rb | 2 +- .../models/payroll/earning_template.rb | 26 +++++++++++++++++++ lib/xeroizer/models/payroll/pay_template.rb | 6 +++++ lib/xeroizer/payroll_application.rb | 1 + 4 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 lib/xeroizer/models/payroll/earning_template.rb diff --git a/lib/xeroizer.rb b/lib/xeroizer.rb index 1050c3ef..fb00a02f 100644 --- a/lib/xeroizer.rb +++ b/lib/xeroizer.rb @@ -45,7 +45,7 @@ 'leave_line', 'reimbursement_line', 'super_line', 'deduction_line', 'earnings_line', 'opening_balance', 'pay_run', 'settings', 'tracking_categories', 'employee_groups', 'timesheet_categories', 'account', 'tax_declaration', 'payslip', 'timesheet_earnings_line', 'tax_line', 'leave_accrual_line', 'superannuation_line', - 'leave_balance', 'time_off_balance', 'earnings_type', 'super_fund'].each do |payroll_model| + 'leave_balance', 'time_off_balance', 'earnings_type', 'super_fund', 'earning_template'].each do |payroll_model| require "xeroizer/models/payroll/#{payroll_model}" end diff --git a/lib/xeroizer/models/payroll/earning_template.rb b/lib/xeroizer/models/payroll/earning_template.rb new file mode 100644 index 00000000..5426da6b --- /dev/null +++ b/lib/xeroizer/models/payroll/earning_template.rb @@ -0,0 +1,26 @@ +module Xeroizer + module Record + module Payroll + + class EarningTemplateModel < PayrollBaseModel + + set_permissions :read, :write, :update + + end + # https://developer.xero.com/documentation/payroll-api-uk/employeepaytemplates + class EarningTemplate < PayrollBase + + guid :pay_template_earning_id + guid :earnings_rate_id + + string :name + + decimal :rate_per_unit + decimal :number_of_units + decimal :fixed_amount + + end + + end + end +end diff --git a/lib/xeroizer/models/payroll/pay_template.rb b/lib/xeroizer/models/payroll/pay_template.rb index 2de96266..95675e75 100644 --- a/lib/xeroizer/models/payroll/pay_template.rb +++ b/lib/xeroizer/models/payroll/pay_template.rb @@ -4,6 +4,8 @@ module Payroll class PayTemplateModel < PayrollBaseModel + set_permissions :read, :write, :update + set_standalone_model true set_xml_root_name 'PayTemplate' set_xml_node_name 'PayTemplate' @@ -23,6 +25,10 @@ class PayTemplate < PayrollBase has_many :reimbursement_lines has_many :leave_lines + # UK: https://developer.xero.com/documentation/payroll-api-uk/employeepaytemplates + has_many :earning_templates + guid :employee_id + end end diff --git a/lib/xeroizer/payroll_application.rb b/lib/xeroizer/payroll_application.rb index f09b503c..f5de8f9c 100644 --- a/lib/xeroizer/payroll_application.rb +++ b/lib/xeroizer/payroll_application.rb @@ -40,6 +40,7 @@ def self.record(record_type) record :LeaveBalance # UK, NZ record :TrackingCategories # UK record :EmployeeLeaveType # UK + record :PayTemplate # UK def initialize(application) From 1cf440e0d604426c10bee172665bc1702936ebab Mon Sep 17 00:00:00 2001 From: Philip Fraser Date: Tue, 30 Jul 2019 07:35:39 +0200 Subject: [PATCH 78/82] Add salary and wages --- lib/xeroizer.rb | 2 +- .../models/payroll/salary_and_wages_type.rb | 32 +++++++++++++++++++ lib/xeroizer/payroll_application.rb | 1 + 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 lib/xeroizer/models/payroll/salary_and_wages_type.rb diff --git a/lib/xeroizer.rb b/lib/xeroizer.rb index fb00a02f..3f41e51d 100644 --- a/lib/xeroizer.rb +++ b/lib/xeroizer.rb @@ -45,7 +45,7 @@ 'leave_line', 'reimbursement_line', 'super_line', 'deduction_line', 'earnings_line', 'opening_balance', 'pay_run', 'settings', 'tracking_categories', 'employee_groups', 'timesheet_categories', 'account', 'tax_declaration', 'payslip', 'timesheet_earnings_line', 'tax_line', 'leave_accrual_line', 'superannuation_line', - 'leave_balance', 'time_off_balance', 'earnings_type', 'super_fund', 'earning_template'].each do |payroll_model| + 'leave_balance', 'time_off_balance', 'earnings_type', 'super_fund', 'earning_template', 'salary_and_wages'].each do |payroll_model| require "xeroizer/models/payroll/#{payroll_model}" end diff --git a/lib/xeroizer/models/payroll/salary_and_wages_type.rb b/lib/xeroizer/models/payroll/salary_and_wages_type.rb new file mode 100644 index 00000000..cacf6d42 --- /dev/null +++ b/lib/xeroizer/models/payroll/salary_and_wages_type.rb @@ -0,0 +1,32 @@ +module Xeroizer + module Record + module Payroll + + class SalaryAndWagesModel < PayrollBaseModel + + set_permissions :read, :write, :update + + def api_url(options = {}) + "employees/#{options.delete(:employee_id)}/salaryAndWages" + end + end + + class SalaryAndWages < PayrollBase + + guid :salary_and_wages_id + guid :earnings_rate_id + + decimal :number_of_units_per_week + decimal :rate_per_unit + decimal :number_of_units_per_day + decimal :annual_salary + + datetime_utc :effective_from + + string :status + string :payment_type + end + + end + end +end diff --git a/lib/xeroizer/payroll_application.rb b/lib/xeroizer/payroll_application.rb index f5de8f9c..2dc193ea 100644 --- a/lib/xeroizer/payroll_application.rb +++ b/lib/xeroizer/payroll_application.rb @@ -41,6 +41,7 @@ def self.record(record_type) record :TrackingCategories # UK record :EmployeeLeaveType # UK record :PayTemplate # UK + record :SalaryAndWages # UK def initialize(application) From 7548cc0fcdb0cfb33b8551e6a983415790d08dcb Mon Sep 17 00:00:00 2001 From: Philip Fraser Date: Tue, 30 Jul 2019 07:43:54 +0200 Subject: [PATCH 79/82] rename salary and wages file --- .../payroll/{salary_and_wages_type.rb => salary_and_wages.rb} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename lib/xeroizer/models/payroll/{salary_and_wages_type.rb => salary_and_wages.rb} (100%) diff --git a/lib/xeroizer/models/payroll/salary_and_wages_type.rb b/lib/xeroizer/models/payroll/salary_and_wages.rb similarity index 100% rename from lib/xeroizer/models/payroll/salary_and_wages_type.rb rename to lib/xeroizer/models/payroll/salary_and_wages.rb From 3de5ce27e03f9904c3f4c7875e6abfc30f433cf0 Mon Sep 17 00:00:00 2001 From: Daniel O'Connor Date: Fri, 18 Oct 2019 03:31:50 +1030 Subject: [PATCH 80/82] Swap prefix --- lib/xeroizer/partner_application.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/xeroizer/partner_application.rb b/lib/xeroizer/partner_application.rb index 03bffa1e..53660194 100644 --- a/lib/xeroizer/partner_application.rb +++ b/lib/xeroizer/partner_application.rb @@ -17,9 +17,7 @@ class PartnerApplication < GenericApplication # @return [PartnerApplication] instance of PrivateApplication def initialize(consumer_key, consumer_secret, path_to_private_key, options = {}) default_options = { - :xero_url => 'https://api.xero.com/api.xro/2.0', - # TODO should this prefix replace the xero_url but be api.xero.com? - :xero_url_prefix => 'https://api-partner.network.xero.com', + :xero_url_prefix => 'https://api.xero.com', :site => 'https://api.xero.com', :authorize_url => 'https://api.xero.com/oauth/Authorize', :signature_method => 'RSA-SHA1' From ce602f341f7ea57b4d851c17008c00cca8300f15 Mon Sep 17 00:00:00 2001 From: Daniel O'Connor Date: Fri, 18 Oct 2019 04:54:45 +1030 Subject: [PATCH 81/82] Remove doubled requires --- lib/xeroizer.rb | 39 --------------------------------------- 1 file changed, 39 deletions(-) diff --git a/lib/xeroizer.rb b/lib/xeroizer.rb index 626459e0..21fd7a0d 100644 --- a/lib/xeroizer.rb +++ b/lib/xeroizer.rb @@ -80,45 +80,6 @@ require "xeroizer/models/payroll/#{payroll_model}" end -# TODO Remove? -require 'xeroizer/models/account' -require 'xeroizer/models/address' -require 'xeroizer/models/allocation' -require 'xeroizer/models/branding_theme' -require 'xeroizer/models/bank_transaction' -require 'xeroizer/models/bank_account' -require 'xeroizer/models/contact' -require 'xeroizer/models/contact_group' -require 'xeroizer/models/credit_note' -require 'xeroizer/models/currency' -require 'xeroizer/models/employee' -require 'xeroizer/models/expense_claim' -require 'xeroizer/models/invoice' -require 'xeroizer/models/item' -require 'xeroizer/models/item_purchase_details' -require 'xeroizer/models/item_sales_details' -require 'xeroizer/models/journal' -require 'xeroizer/models/journal_line' -require 'xeroizer/models/line_item' -require 'xeroizer/models/manual_journal' -require 'xeroizer/models/manual_journal_line' -require 'xeroizer/models/option' -require 'xeroizer/models/organisation' -require 'xeroizer/models/payment' -require 'xeroizer/models/prepayment' -require 'xeroizer/models/phone' -require 'xeroizer/models/receipt' -require 'xeroizer/models/repeating_invoice' -require 'xeroizer/models/schedule' -require 'xeroizer/models/tax_rate' -require 'xeroizer/models/tax_component' -require 'xeroizer/models/tracking_category' -require 'xeroizer/models/tracking_category_child' -require 'xeroizer/models/user' -require 'xeroizer/models/journal_line_tracking_category' -require 'xeroizer/models/contact_sales_tracking_category' -require 'xeroizer/models/contact_purchases_tracking_category' - require 'xeroizer/report/factory' require 'xeroizer/response' From dffc119f0366654d1615876404d92b7755c5ce6b Mon Sep 17 00:00:00 2001 From: Daniel O'Connor Date: Fri, 18 Oct 2019 04:59:09 +1030 Subject: [PATCH 82/82] DRY require statements --- lib/xeroizer.rb | 42 +++++++++--------------------------------- 1 file changed, 9 insertions(+), 33 deletions(-) diff --git a/lib/xeroizer.rb b/lib/xeroizer.rb index 21fd7a0d..d84bf547 100644 --- a/lib/xeroizer.rb +++ b/lib/xeroizer.rb @@ -30,42 +30,16 @@ require 'xeroizer/configuration' # Include models -require 'xeroizer/models/batch_payment' -require 'xeroizer/models/from_bank_account' -require 'xeroizer/models/to_bank_account' -require 'xeroizer/models/bank_transfer' -require 'xeroizer/models/expense_claim' -require 'xeroizer/models/invoice_reminder' -require 'xeroizer/models/online_invoice' -require 'xeroizer/models/payment_service' -require 'xeroizer/models/prepayment' -require 'xeroizer/models/overpayment' -require 'xeroizer/models/purchase_order' -require 'xeroizer/models/receipt' -require 'xeroizer/models/repeating_invoice' -require 'xeroizer/models/schedule' -require 'xeroizer/models/tax_component' -require 'xeroizer/models/contact_sales_tracking_category' -require 'xeroizer/models/contact_purchases_tracking_category' - -require 'xeroizer/models/payroll/benefit_line' -require 'xeroizer/models/payroll/benefit_type' -require 'xeroizer/models/payroll/earnings_type' -require 'xeroizer/models/payroll/address' -require 'xeroizer/models/payroll/payment_method' -require 'xeroizer/models/payroll/pay_schedule' -require 'xeroizer/models/payroll/paystub' -require 'xeroizer/models/payroll/salary_and_wage' -require 'xeroizer/models/payroll/time_off_line' -require 'xeroizer/models/payroll/time_off_type' -require 'xeroizer/models/payroll/work_location' - -# TODO Merge in above lists ['account','address','allocation','branding_theme','bank_transaction','bank_account','contact','contact_group', 'credit_note','currency','employee','invoice','item','item_purchase_details','item_sales_details', 'journal','journal_line','line_item','manual_journal','manual_journal_line','option','organisation', 'payment','phone','tax_rate','tracking_category','tracking_category_child', - 'journal_line_tracking_category', 'user'].each do |model| + 'journal_line_tracking_category', 'user', 'batch_payment', 'from_bank_account', + 'to_bank_account', 'bank_transfer', 'expense_claim', 'invoice_reminder', + 'online_invoice', 'payment_service', 'prepayment', 'overpayment', + 'purchase_order', 'receipt', 'repeating_invoice', + 'schedule', 'tax_component', 'contact_sales_tracking_category', + 'contact_purchases_tracking_category'].each do |model| require "xeroizer/models/#{model}" end @@ -76,7 +50,9 @@ 'leave_line', 'reimbursement_line', 'super_line', 'deduction_line', 'earnings_line', 'opening_balance', 'pay_run', 'settings', 'tracking_categories', 'employee_groups', 'timesheet_categories', 'account', 'tax_declaration', 'payslip', 'timesheet_earnings_line', 'tax_line', 'leave_accrual_line', 'superannuation_line', - 'leave_balance', 'time_off_balance', 'earnings_type', 'super_fund', 'earning_template', 'salary_and_wages'].each do |payroll_model| + 'leave_balance', 'time_off_balance', 'earnings_type', 'super_fund', 'earning_template', 'salary_and_wages', + 'benefit_line', 'benefit_type', 'earnings_type', 'address', 'payment_method', 'pay_schedule', 'paystub', 'salary_and_wage', + 'time_off_line', 'time_off_type', 'work_location'].each do |payroll_model| require "xeroizer/models/payroll/#{payroll_model}" end