From 68c6b8145c7b5e033f5b29a46fa0f1a244f6f7ab Mon Sep 17 00:00:00 2001 From: Kevin Carter Date: Wed, 28 Aug 2013 11:51:11 -0600 Subject: [PATCH 1/3] Fix whitespace --- lib/quickeebooks/windows/model/employee.rb | 108 +++++++++--------- .../windows/model/time_activity.rb | 94 ++++++++------- 2 files changed, 97 insertions(+), 105 deletions(-) diff --git a/lib/quickeebooks/windows/model/employee.rb b/lib/quickeebooks/windows/model/employee.rb index 6e03e34..7ac1da0 100644 --- a/lib/quickeebooks/windows/model/employee.rb +++ b/lib/quickeebooks/windows/model/employee.rb @@ -9,66 +9,62 @@ module Quickeebooks - module Windows - module Model - class Employee < Quickeebooks::Windows::Model::IntuitType + module Windows + module Model + class Employee < Quickeebooks::Windows::Model::IntuitType - XML_COLLECTION_NODE = 'Employees' - XML_NODE = 'Employee' - - # https://services.intuit.com/sb/employee/v2/ - REST_RESOURCE = "employee" - - xml_convention :camelcase - xml_accessor :id, :from => 'Id', :as => Quickeebooks::Windows::Model::Id - xml_accessor :sync_token, :from => 'SyncToken', :as => Integer - xml_accessor :meta_data, :from => 'MetaData', :as => Quickeebooks::Windows::Model::MetaData - xml_accessor :external_key, :from => 'ExternalKey' - xml_accessor :synchronized, :from => 'Synchronized' - xml_accessor :custom_fields, :from => 'CustomField', :as => [Quickeebooks::Windows::Model::CustomField] - xml_accessor :draft - xml_accessor :object_state, :from => 'ObjectState' - xml_accessor :party_reference_id, :from => 'PartyReferenceId' - xml_accessor :type_of, :from => 'TypeOf' - xml_accessor :name, :from => 'Name' - xml_accessor :addresses, :from => 'Address', :as => [Quickeebooks::Windows::Model::Address] - xml_accessor :phone, :from => 'Phone', :as => [Quickeebooks::Windows::Model::Phone] - xml_accessor :web_site, :from => 'WebSite', :as => Quickeebooks::Windows::Model::WebSite - xml_accessor :email, :from => 'Email', :as => Quickeebooks::Windows::Model::Email - xml_accessor :title, :from => 'Title' - xml_accessor :given_name, :from => 'GivenName' - xml_accessor :middle_name, :from => 'MiddleName' - xml_accessor :family_name, :from => 'FamilyName' - xml_accessor :suffix, :from => 'Suffix' - xml_accessor :gender, :from => 'Gender' - xml_accessor :dba_name, :from => 'DBAName' - xml_accessor :tax_identifier, :from => 'TaxIdentifier' - xml_accessor :notes, :from => 'Note', :as => [Quickeebooks::Windows::Model::Note] - xml_accessor :active, :from => 'Active' - xml_accessor :show_as, :from => 'ShowAs' - xml_accessor :employee_type, :from => 'EmployeeType' - xml_accessor :hired_date, :from => 'HiredDate' - xml_accessor :released_date, :from => 'ReleasedDate' - xml_accessor :use_time_entry, :from => 'UseTimeEntry' - - def address=(address) - self.addresses ||= [] - self.addresses << address - end + XML_COLLECTION_NODE = 'Employees' + XML_NODE = 'Employee' - def billing_address - addresses.detect { |address| address.tag == "Billing" } - end + # https://services.intuit.com/sb/employee/v2/ + REST_RESOURCE = "employee" - def shipping_address - addresses.detect { |address| address.tag == "Shipping" } - end + xml_convention :camelcase + xml_accessor :id, :from => 'Id', :as => Quickeebooks::Windows::Model::Id + xml_accessor :sync_token, :from => 'SyncToken', :as => Integer + xml_accessor :meta_data, :from => 'MetaData', :as => Quickeebooks::Windows::Model::MetaData + xml_accessor :external_key, :from => 'ExternalKey' + xml_accessor :synchronized, :from => 'Synchronized' + xml_accessor :custom_fields, :from => 'CustomField', :as => [Quickeebooks::Windows::Model::CustomField] + xml_accessor :draft + xml_accessor :object_state, :from => 'ObjectState' + xml_accessor :party_reference_id, :from => 'PartyReferenceId' + xml_accessor :type_of, :from => 'TypeOf' + xml_accessor :name, :from => 'Name' + xml_accessor :addresses, :from => 'Address', :as => [Quickeebooks::Windows::Model::Address] + xml_accessor :phone, :from => 'Phone', :as => [Quickeebooks::Windows::Model::Phone] + xml_accessor :web_site, :from => 'WebSite', :as => Quickeebooks::Windows::Model::WebSite + xml_accessor :email, :from => 'Email', :as => Quickeebooks::Windows::Model::Email + xml_accessor :title, :from => 'Title' + xml_accessor :given_name, :from => 'GivenName' + xml_accessor :middle_name, :from => 'MiddleName' + xml_accessor :family_name, :from => 'FamilyName' + xml_accessor :suffix, :from => 'Suffix' + xml_accessor :gender, :from => 'Gender' + xml_accessor :dba_name, :from => 'DBAName' + xml_accessor :tax_identifier, :from => 'TaxIdentifier' + xml_accessor :notes, :from => 'Note', :as => [Quickeebooks::Windows::Model::Note] + xml_accessor :active, :from => 'Active' + xml_accessor :show_as, :from => 'ShowAs' + xml_accessor :employee_type, :from => 'EmployeeType' + xml_accessor :hired_date, :from => 'HiredDate' + xml_accessor :released_date, :from => 'ReleasedDate' + xml_accessor :use_time_entry, :from => 'UseTimeEntry' - end - end - end -end + def address=(address) + self.addresses ||= [] + self.addresses << address + end + def billing_address + addresses.detect { |address| address.tag == "Billing" } + end + def shipping_address + addresses.detect { |address| address.tag == "Shipping" } + end - \ No newline at end of file + end + end + end +end diff --git a/lib/quickeebooks/windows/model/time_activity.rb b/lib/quickeebooks/windows/model/time_activity.rb index 36c5bf3..ce0c9ae 100644 --- a/lib/quickeebooks/windows/model/time_activity.rb +++ b/lib/quickeebooks/windows/model/time_activity.rb @@ -11,56 +11,52 @@ module Quickeebooks - module Windows - module Model - class TimeActivity < Quickeebooks::Windows::Model::IntuitType + module Windows + module Model + class TimeActivity < Quickeebooks::Windows::Model::IntuitType - XML_COLLECTION_NODE = 'TimeActivities' - XML_NODE = 'TimeActivity' - - # https://services.intuit.com/sb/timeactivity/v2/ - REST_RESOURCE = "timeactivity" - - xml_convention :camelcase - xml_accessor :id, :from => 'Id', :as => Quickeebooks::Windows::Model::Id - xml_accessor :sync_token, :from => 'SyncToken', :as => Integer - xml_accessor :meta_data, :from => 'MetaData', :as => Quickeebooks::Windows::Model::MetaData - xml_accessor :external_key, :from => 'ExternalKey' - xml_accessor :synchronized, :from => 'Synchronized' - xml_accessor :custom_fields, :from => 'CustomField', :as => [Quickeebooks::Windows::Model::CustomField] - xml_accessor :draft - xml_accessor :object_state, :from => 'ObjectState' - xml_accessor :txn_date, :from => 'TxnDate' - xml_accessor :name_of, :from => 'NameOf' - xml_accessor :employee, :from => 'Employee', :as => Quickeebooks::Windows::Model::TimeActivityEmployee - xml_accessor :vendor, :from => 'Vendor', :as => Quickeebooks::Windows::Model::TimeActivityVendor - xml_accessor :customer_id, :from => 'CustomerId' - xml_accessor :customer_name, :from => 'CustomerName' - xml_accessor :job_id, :from => 'JobId', :as => Quickeebooks::Windows::Model::Id - xml_accessor :job_name, :from => 'JobName' - xml_accessor :item_id, :from => 'ItemId', :as => Quickeebooks::Windows::Model::Id - xml_accessor :item_name, :from => 'ItemName' - xml_accessor :item_type, :from => 'ItemType' - xml_accessor :class_id, :from => 'ClassId', :as => Quickeebooks::Windows::Model::Id - xml_accessor :pay_item_id, :from => 'PayItemId', :as => Quickeebooks::Windows::Model::Id - xml_accessor :pay_item_name, :from => 'PayItemName' - xml_accessor :billable_status, :from => 'BillableStatus' - xml_accessor :taxable, :from => 'Taxable' - xml_accessor :hourly_rate, :from => 'HourlyRate' - xml_accessor :hours, :from => 'Hours' - xml_accessor :minutes, :from => 'Minutes' - xml_accessor :seconds, :from => 'Seconds' - xml_accessor :break_hours, :from => 'BreakHours' - xml_accessor :break_minutes, :from => 'BreakMinutes' - xml_accessor :start_time, :from => 'StartTime' - xml_accessor :end_time, :from => 'EndTime' - xml_accessor :description, :from => 'Description' - - end - end - end -end + XML_COLLECTION_NODE = 'TimeActivities' + XML_NODE = 'TimeActivity' + # https://services.intuit.com/sb/timeactivity/v2/ + REST_RESOURCE = "timeactivity" + xml_convention :camelcase + xml_accessor :id, :from => 'Id', :as => Quickeebooks::Windows::Model::Id + xml_accessor :sync_token, :from => 'SyncToken', :as => Integer + xml_accessor :meta_data, :from => 'MetaData', :as => Quickeebooks::Windows::Model::MetaData + xml_accessor :external_key, :from => 'ExternalKey' + xml_accessor :synchronized, :from => 'Synchronized' + xml_accessor :custom_fields, :from => 'CustomField', :as => [Quickeebooks::Windows::Model::CustomField] + xml_accessor :draft + xml_accessor :object_state, :from => 'ObjectState' + xml_accessor :txn_date, :from => 'TxnDate' + xml_accessor :name_of, :from => 'NameOf' + xml_accessor :employee, :from => 'Employee', :as => Quickeebooks::Windows::Model::TimeActivityEmployee + xml_accessor :vendor, :from => 'Vendor', :as => Quickeebooks::Windows::Model::TimeActivityVendor + xml_accessor :customer_id, :from => 'CustomerId' + xml_accessor :customer_name, :from => 'CustomerName' + xml_accessor :job_id, :from => 'JobId', :as => Quickeebooks::Windows::Model::Id + xml_accessor :job_name, :from => 'JobName' + xml_accessor :item_id, :from => 'ItemId', :as => Quickeebooks::Windows::Model::Id + xml_accessor :item_name, :from => 'ItemName' + xml_accessor :item_type, :from => 'ItemType' + xml_accessor :class_id, :from => 'ClassId', :as => Quickeebooks::Windows::Model::Id + xml_accessor :pay_item_id, :from => 'PayItemId', :as => Quickeebooks::Windows::Model::Id + xml_accessor :pay_item_name, :from => 'PayItemName' + xml_accessor :billable_status, :from => 'BillableStatus' + xml_accessor :taxable, :from => 'Taxable' + xml_accessor :hourly_rate, :from => 'HourlyRate' + xml_accessor :hours, :from => 'Hours' + xml_accessor :minutes, :from => 'Minutes' + xml_accessor :seconds, :from => 'Seconds' + xml_accessor :break_hours, :from => 'BreakHours' + xml_accessor :break_minutes, :from => 'BreakMinutes' + xml_accessor :start_time, :from => 'StartTime' + xml_accessor :end_time, :from => 'EndTime' + xml_accessor :description, :from => 'Description' - \ No newline at end of file + end + end + end +end From 8d463825806203572640488717b0f544553b4363 Mon Sep 17 00:00:00 2001 From: Kevin Carter Date: Wed, 28 Aug 2013 12:25:36 -0600 Subject: [PATCH 2/3] Add create operation to Windows#Employee --- README.md | 2 +- lib/quickeebooks/windows/model/employee.rb | 18 +++++ lib/quickeebooks/windows/service/employee.rb | 15 +++- .../windows/services/employee_spec.rb | 72 ++++++++++++++++++- spec/xml/windows/employee_create_success.xml | 13 ++++ 5 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 spec/xml/windows/employee_create_success.xml diff --git a/README.md b/README.md index 6d55965..310a901 100644 --- a/README.md +++ b/README.md @@ -391,7 +391,7 @@ Bill | no | no | no | no | no Bill Payment | no | no | no | no | no Company Meta Data | no | no | no | no | no | `load` Customer | yes | yes | yes | no | yes | -Employee | no | no | yes | no | no | +Employee | yes | no | yes | no | no | Entitlement | n/a | n/a | n/a | n/a | n/a | Invoice | yes | yes | yes | no | yes | Item | yes | no | yes | no | yes | diff --git a/lib/quickeebooks/windows/model/employee.rb b/lib/quickeebooks/windows/model/employee.rb index 7ac1da0..66c8507 100644 --- a/lib/quickeebooks/windows/model/employee.rb +++ b/lib/quickeebooks/windows/model/employee.rb @@ -12,6 +12,7 @@ module Quickeebooks module Windows module Model class Employee < Quickeebooks::Windows::Model::IntuitType + include ActiveModel::Validations XML_COLLECTION_NODE = 'Employees' XML_NODE = 'Employee' @@ -51,6 +52,23 @@ class Employee < Quickeebooks::Windows::Model::IntuitType xml_accessor :released_date, :from => 'ReleasedDate' xml_accessor :use_time_entry, :from => 'UseTimeEntry' + validates_length_of :name, :minimum => 1 + validate :name_cannot_contain_invalid_characters + + def valid_for_create? + valid? + if type_of.nil? + errors.add(:type_of, "Missing required attribute TypeOf for Create") + end + errors.empty? + end + + def name_cannot_contain_invalid_characters + if name.to_s.index(':') + errors.add(:name, "Name cannot contain a colon (:)") + end + end + def address=(address) self.addresses ||= [] self.addresses << address diff --git a/lib/quickeebooks/windows/service/employee.rb b/lib/quickeebooks/windows/service/employee.rb index fe36a22..3803f75 100644 --- a/lib/quickeebooks/windows/service/employee.rb +++ b/lib/quickeebooks/windows/service/employee.rb @@ -8,7 +8,20 @@ class Employee < Quickeebooks::Windows::Service::ServiceBase def list(filters = [], page = 1, per_page = 20, sort = nil, options = {}) fetch_collection(Quickeebooks::Windows::Model::Employee, nil, filters, page, per_page, sort, options) end + + def create(employee) + raise InvalidModelException unless employee.valid_for_create? + + # XML is a wrapped 'object' where the type is specified as an attribute + # + xml_node = employee.to_xml(:name => 'Object') + xml_node.set_attribute('xsi:type', 'Employee') + xml = Quickeebooks::Shared::Service::OperationNode.new.add do |content| + content << "#{self.realm_id}#{xml_node}" + end + perform_write(Quickeebooks::Windows::Model::Employee, xml) + end end end end -end \ No newline at end of file +end diff --git a/spec/quickeebooks/windows/services/employee_spec.rb b/spec/quickeebooks/windows/services/employee_spec.rb index f89a2b3..a3e6735 100644 --- a/spec/quickeebooks/windows/services/employee_spec.rb +++ b/spec/quickeebooks/windows/services/employee_spec.rb @@ -39,4 +39,74 @@ end -end \ No newline at end of file + it "cannot create a employee without a name" do + employee = Quickeebooks::Windows::Model::Employee.new + service = Quickeebooks::Windows::Service::Employee.new + service.access_token = @oauth + service.realm_id = @realm_id + lambda do + service.create(employee) + end.should raise_error(InvalidModelException) + + employee.valid?.should == false + employee.errors.keys.include?(:name).should == true + end + + it "cannot create a employee without a type_of attribute" do + employee = Quickeebooks::Windows::Model::Employee.new + service = Quickeebooks::Windows::Service::Employee.new + service.access_token = @oauth + service.realm_id = @realm_id + lambda do + service.create(employee) + end.should raise_error(InvalidModelException) + employee.errors.keys.include?(:type_of).should == true + end + + it "cannot create a employee with invalid name" do + employee = Quickeebooks::Windows::Model::Employee.new + service = Quickeebooks::Windows::Service::Employee.new + service.access_token = @oauth + service.realm_id = @realm_id + employee.name = "Bobs:Plumbing" + lambda do + service.create(employee) + end.should raise_error(InvalidModelException) + + employee.valid?.should == false + employee.errors.keys.include?(:name).should == true + end + + it "can create a employee" do + xml = windowsFixture("employee_create_success.xml") + service = Quickeebooks::Windows::Service::Employee.new + model = Quickeebooks::Windows::Model::Employee + employee = Quickeebooks::Windows::Model::Employee.new + + service.access_token = @oauth + service.realm_id = @realm_id + FakeWeb.register_uri(:post, service.url_for_resource(model::REST_RESOURCE), :status => ["200", "OK"], :body => xml) + + employee.type_of = "Person" + employee.name = "Bugs Bunny" + employee.given_name = "Bugs" + employee.family_name = "Bunny" + email = Quickeebooks::Windows::Model::Email.new + email.address = "foo@bar.com" + email.tag = "Business" + employee.email = email + address = Quickeebooks::Windows::Model::Address.new + address.tag = "Billing" + address.line1 = "123 Main St." + address.city = "San Francisco" + address.country_sub_division_code = "CA" + address.postal_code = "94117" + address.country = "USA" + employee.address = address + create_response = service.create(employee) + create_response.success?.should == true + create_response.success.party_role_ref.id.value.should == "155031" + create_response.success.request_name.should == "EmployeeAdd" + end + +end diff --git a/spec/xml/windows/employee_create_success.xml b/spec/xml/windows/employee_create_success.xml new file mode 100644 index 0000000..09deb07 --- /dev/null +++ b/spec/xml/windows/employee_create_success.xml @@ -0,0 +1,13 @@ + + + + + 155031 + 1 + 2013-08-28T18:14:30Z + 1021971 + + EmployeeAdd + 2013-08-28T18:14:30Z + + From 4020f873856a92e4a746ad31aa8b480b3a65d7b8 Mon Sep 17 00:00:00 2001 From: Kevin Carter Date: Wed, 28 Aug 2013 12:53:04 -0600 Subject: [PATCH 3/3] Add create operation to Windows#TimeActivity --- README.md | 2 +- .../windows/model/time_activity.rb | 17 ++++++++ .../windows/service/time_activity.rb | 15 ++++++- .../windows/services/time_activity_spec.rb | 42 ++++++++++++++++++- .../windows/time_activity_create_success.xml | 12 ++++++ 5 files changed, 85 insertions(+), 3 deletions(-) create mode 100644 spec/xml/windows/time_activity_create_success.xml diff --git a/README.md b/README.md index 310a901..bb71d0d 100644 --- a/README.md +++ b/README.md @@ -404,7 +404,7 @@ Sales Tax | no | no | yes | no | no | Ship Method | no | no | yes | no | no | Sync Activity | no | no | no | no | no | `retrieve` Sync Status | no | no | no | no | no | `retrieve` -Time Activity | no | no | yes | no | no | +Time Activity | yes | no | yes | no | no | Tracking Class | n/a | n/a | n/a | n/a | n/a | Vendor | no | no | no | no | no | diff --git a/lib/quickeebooks/windows/model/time_activity.rb b/lib/quickeebooks/windows/model/time_activity.rb index ce0c9ae..e60f24c 100644 --- a/lib/quickeebooks/windows/model/time_activity.rb +++ b/lib/quickeebooks/windows/model/time_activity.rb @@ -14,6 +14,7 @@ module Quickeebooks module Windows module Model class TimeActivity < Quickeebooks::Windows::Model::IntuitType + include ActiveModel::Validations XML_COLLECTION_NODE = 'TimeActivities' XML_NODE = 'TimeActivity' @@ -56,6 +57,22 @@ class TimeActivity < Quickeebooks::Windows::Model::IntuitType xml_accessor :end_time, :from => 'EndTime' xml_accessor :description, :from => 'Description' + validates_inclusion_of :name_of, :in => %w(Employee Vendor) + validates_inclusion_of :billable_status, :in => %w(Billable NotBillable HasBeenBilled), + :allow_blank => true + validate :duration_is_set + + def valid_for_create? + valid? + errors.empty? + end + + def duration_is_set + unless (self.hours || self.minutes || self.seconds) + errors.add(:base, 'A duration is required') + end + end + end end end diff --git a/lib/quickeebooks/windows/service/time_activity.rb b/lib/quickeebooks/windows/service/time_activity.rb index 0558872..4227b37 100644 --- a/lib/quickeebooks/windows/service/time_activity.rb +++ b/lib/quickeebooks/windows/service/time_activity.rb @@ -10,7 +10,20 @@ def list(filters = [], page = 1, per_page = 20, sort = nil, options = {}) fetch_collection(Quickeebooks::Windows::Model::TimeActivity, nil, filters, page, per_page, sort, options) end + def create(time_activity) + raise InvalidModelException unless time_activity.valid_for_create? + + # XML is a wrapped 'object' where the type is specified as an attribute + # + xml_node = time_activity.to_xml(:name => 'Object') + xml_node.set_attribute('xsi:type', 'TimeActivity') + xml = Quickeebooks::Shared::Service::OperationNode.new.add do |content| + content << "#{self.realm_id}#{xml_node}" + end + perform_write(Quickeebooks::Windows::Model::TimeActivity, xml) + end + end end end -end \ No newline at end of file +end diff --git a/spec/quickeebooks/windows/services/time_activity_spec.rb b/spec/quickeebooks/windows/services/time_activity_spec.rb index df9be4a..b47a826 100644 --- a/spec/quickeebooks/windows/services/time_activity_spec.rb +++ b/spec/quickeebooks/windows/services/time_activity_spec.rb @@ -35,4 +35,44 @@ my_time.employee.employee_id.value.should == "246" end -end \ No newline at end of file + it "cannot create a time activity if invalid" do + time_activity = Quickeebooks::Windows::Model::TimeActivity.new + service = Quickeebooks::Windows::Service::TimeActivity.new + service.access_token = @oauth + service.realm_id = @realm_id + lambda do + service.create(time_activity) + end.should raise_error(InvalidModelException) + + time_activity.valid?.should == false + time_activity.errors.keys.should include(:name_of) + end + + it "can create a time_activity" do + xml = windowsFixture("time_activity_create_success.xml") + service = Quickeebooks::Windows::Service::TimeActivity.new + model = Quickeebooks::Windows::Model::TimeActivity + time_activity = Quickeebooks::Windows::Model::TimeActivity.new + + service.access_token = @oauth + service.realm_id = @realm_id + FakeWeb.register_uri(:post, service.url_for_resource(model::REST_RESOURCE), :status => ["200", "OK"], :body => xml) + + time_activity.name_of = 'Employee' + employee = Quickeebooks::Windows::Model::TimeActivityEmployee.new + employee.employee_name = 'Bugs Bunny' + time_activity.employee = employee + time_activity.start_time = "2013-08-14T18:45:00Z" + time_activity.end_time = "2013-08-14T19:15:22Z" + time_activity.txn_date = "2013-08-14" + time_activity.hours = 0 + time_activity.minutes = 30 + time_activity.seconds = 0 + + create_response = service.create(time_activity) + create_response.success?.should == true + create_response.success.object_ref.id.value.should == "517536" + create_response.success.request_name.should == "TimeActivityAdd" + end + +end diff --git a/spec/xml/windows/time_activity_create_success.xml b/spec/xml/windows/time_activity_create_success.xml new file mode 100644 index 0000000..d13c586 --- /dev/null +++ b/spec/xml/windows/time_activity_create_success.xml @@ -0,0 +1,12 @@ + + + + + 517536 + 1 + 2013-08-28T18:49:23Z + + TimeActivityAdd + 2013-08-28T18:49:23Z + +