diff --git a/lib/netsuite.rb b/lib/netsuite.rb
index 85ccee065..679006ddb 100644
--- a/lib/netsuite.rb
+++ b/lib/netsuite.rb
@@ -46,6 +46,7 @@ module Support
module Actions
autoload :Add, 'netsuite/actions/add'
+ autoload :AttachFile, 'netsuite/actions/attach_file'
autoload :Delete, 'netsuite/actions/delete'
autoload :DeleteList, 'netsuite/actions/delete_list'
autoload :Get, 'netsuite/actions/get'
diff --git a/lib/netsuite/actions/attach_file.rb b/lib/netsuite/actions/attach_file.rb
new file mode 100644
index 000000000..e25720c36
--- /dev/null
+++ b/lib/netsuite/actions/attach_file.rb
@@ -0,0 +1,87 @@
+module NetSuite
+ module Actions
+ class AttachFile
+ include Support::Requests
+
+ def initialize(object, file)
+ @object = object
+ @file = file
+ end
+
+ private
+
+ def request(credentials = {})
+ NetSuite::Configuration.connection({}, credentials).call(:attach, :message => request_body)
+ end
+
+ #
+ #
+ #
+ #
+ #
+ #
+ #
+ #
+ #
+
+ def request_body
+ {
+ 'platformCore:attachReference' => {
+ '@xsi:type' => 'platformCore:AttachBasicReference',
+ 'platformCore:attachTo' => {
+ '@internalId' => @object.internal_id,
+ '@type' => @object.type,
+ '@xsi:type' => 'platformCore:RecordRef'
+ },
+ 'platformCore:attachedRecord' => {
+ '@internalId' => @file.internal_id,
+ '@type' => 'file',
+ '@xsi:type' => 'platformCore:RecordRef'
+ }
+ }
+ }
+ end
+
+ def success?
+ @success ||= response_hash[:status][:@is_success] == 'true'
+ end
+
+ def response_body
+ @response_body ||= response_hash[:base_ref]
+ end
+
+ def response_errors
+ if response_hash[:status] && response_hash[:status][:status_detail]
+ @response_errors ||= errors
+ end
+ end
+
+ def response_hash
+ @response_hash ||= @response.to_hash[:attach_response][:write_response]
+ end
+
+ def errors
+ error_obj = response_hash[:status][:status_detail]
+ error_obj = [error_obj] if error_obj.class == Hash
+ error_obj.map do |error|
+ NetSuite::Error.new(error)
+ end
+ end
+
+ module Support
+ def attach_file(file, credentials = {})
+ response = NetSuite::Actions::AttachFile.call([self, file], credentials)
+
+ @errors = response.errors
+
+ if response.success?
+ @internal_id = response.body[:@internal_id]
+ true
+ else
+ false
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/netsuite/records/file.rb b/lib/netsuite/records/file.rb
index a531f5cb1..40fbef22b 100644
--- a/lib/netsuite/records/file.rb
+++ b/lib/netsuite/records/file.rb
@@ -11,7 +11,7 @@ class File < NetSuite::Support::Base
fields :content, :description, :name, :media_type_name, :file_type, :text_file_encoding
- record_refs :klass
+ record_refs :folder, :klass
read_only_fields :url
diff --git a/lib/netsuite/records/invoice.rb b/lib/netsuite/records/invoice.rb
index ca7c30fbd..c5628f489 100644
--- a/lib/netsuite/records/invoice.rb
+++ b/lib/netsuite/records/invoice.rb
@@ -9,7 +9,7 @@ class Invoice
# https://system.netsuite.com/help/helpcenter/en_US/srbrowser/Browser2014_1/schema/record/invoice.html?mode=package
- actions :get, :get_list, :initialize, :add, :update, :delete, :upsert, :search
+ actions :attach_file, :get, :get_list, :initialize, :add, :update, :delete, :upsert, :search
fields :balance, :bill_address,
:billing_schedule, :contrib_pct, :created_date, :currency_name, :custom_field_list,
diff --git a/lib/netsuite/support/actions.rb b/lib/netsuite/support/actions.rb
index bf1b7d1f8..8ebfedf36 100644
--- a/lib/netsuite/support/actions.rb
+++ b/lib/netsuite/support/actions.rb
@@ -18,6 +18,8 @@ def actions(*args)
def action(name)
case name
+ when :attach_file
+ self.send(:include, NetSuite::Actions::AttachFile::Support)
when :get
self.send(:include, NetSuite::Actions::Get::Support)
when :get_all
diff --git a/lib/netsuite/support/records.rb b/lib/netsuite/support/records.rb
index 2291ecf21..c23a8312a 100644
--- a/lib/netsuite/support/records.rb
+++ b/lib/netsuite/support/records.rb
@@ -49,7 +49,15 @@ def to_attributes!(hash, kname, v)
end
def record_type
- "#{record_namespace}:#{self.class.to_s.split('::').last}"
+ "#{record_namespace}:#{record_type_without_namespace}"
+ end
+
+ def type
+ record_type_without_namespace.downcase
+ end
+
+ def record_type_without_namespace
+ "#{self.class.to_s.split('::').last}"
end
def refresh(credentials = {})
diff --git a/spec/netsuite/actions/attach_file_spec.rb b/spec/netsuite/actions/attach_file_spec.rb
new file mode 100644
index 000000000..abdcc9785
--- /dev/null
+++ b/spec/netsuite/actions/attach_file_spec.rb
@@ -0,0 +1,59 @@
+require 'spec_helper'
+
+describe NetSuite::Actions::AttachFile do
+ before(:all) { savon.mock! }
+ after(:all) { savon.unmock! }
+
+ let(:invoice) { NetSuite::Records::Invoice.new(internal_id: 999) }
+ let(:file) { double('file', internal_id: 111) }
+ let(:message) do
+ {
+ 'platformCore:attachReference' => {
+ '@xsi:type' => 'platformCore:AttachBasicReference',
+ 'platformCore:attachTo' => {
+ '@internalId' => invoice.internal_id,
+ '@type' => invoice.type,
+ '@xsi:type' => 'platformCore:RecordRef'
+ },
+ 'platformCore:attachedRecord' => {
+ '@internalId' => file.internal_id,
+ '@type' => 'file',
+ '@xsi:type' => 'platformCore:RecordRef'
+ }
+ }
+ }
+ end
+
+ before do
+ savon.expects(:attach).with(:message => message).returns(envelope)
+ end
+
+ context 'when successful' do
+ let(:envelope) { File.read('spec/support/fixtures/attach/attach_file_to_invoice.xml') }
+
+ it 'returns a valid Response object' do
+ response = NetSuite::Actions::AttachFile.call([invoice, file])
+ expect(response).to be_kind_of(NetSuite::Response)
+ expect(response).to be_success
+ end
+ end
+
+ context 'when not successful' do
+ let(:envelope) { File.read('spec/support/fixtures/attach/attach_file_to_invoice_error.xml') }
+
+ it 'provides an error method on the object with details about the error' do
+ invoice.attach_file(file)
+ error = invoice.errors.first
+
+ expect(error).to be_kind_of(NetSuite::Error)
+ expect(error.type).to eq('ERROR')
+ expect(error.code).to eq('INVALID')
+ expect(error.message).to eq('Invalid request.')
+ end
+
+ it 'provides an error method on the response' do
+ response = NetSuite::Actions::AttachFile.call([invoice, file])
+ expect(response.errors.first).to be_kind_of(NetSuite::Error)
+ end
+ end
+end
diff --git a/spec/netsuite/records/file_spec.rb b/spec/netsuite/records/file_spec.rb
new file mode 100644
index 000000000..4d943d387
--- /dev/null
+++ b/spec/netsuite/records/file_spec.rb
@@ -0,0 +1,11 @@
+require 'spec_helper'
+
+describe NetSuite::Records::File do
+ let(:file) { NetSuite::Records::File.new }
+
+ it 'has all the right record refs' do
+ [:folder, :klass].each do |record_ref|
+ expect(file).to have_record_ref(record_ref)
+ end
+ end
+end
diff --git a/spec/netsuite/records/invoice_spec.rb b/spec/netsuite/records/invoice_spec.rb
index 41701e205..182689eaf 100644
--- a/spec/netsuite/records/invoice_spec.rb
+++ b/spec/netsuite/records/invoice_spec.rb
@@ -198,6 +198,35 @@
end
end
+ describe '#attach_file' do
+ let(:test_data) { { :email => 'test@example.com', :fax => '1234567890' } }
+ let(:file) { double('file') }
+
+ context 'when the response is successful' do
+ let(:response) { NetSuite::Response.new(:success => true, :body => { :internal_id => '1' }) }
+
+ it 'returns true' do
+ invoice = NetSuite::Records::Invoice.new(test_data)
+ expect(NetSuite::Actions::AttachFile).to receive(:call).
+ with([invoice, file], {}).
+ and_return(response)
+ expect(invoice.attach_file(file)).to be_truthy
+ end
+ end
+
+ context 'when the response is unsuccessful' do
+ let(:response) { NetSuite::Response.new(:success => false, :body => {}) }
+
+ it 'returns false' do
+ invoice = NetSuite::Records::Invoice.new(test_data)
+ expect(NetSuite::Actions::AttachFile).to receive(:call).
+ with([invoice, file], {}).
+ and_return(response)
+ expect(invoice.attach_file(file)).to be_falsey
+ end
+ end
+ end
+
describe '#delete' do
context 'when the response is successful' do
let(:response) { NetSuite::Response.new(:success => true, :body => { :internal_id => '1' }) }
diff --git a/spec/support/fixtures/attach/attach_file_to_invoice.xml b/spec/support/fixtures/attach/attach_file_to_invoice.xml
new file mode 100644
index 000000000..c1323db87
--- /dev/null
+++ b/spec/support/fixtures/attach/attach_file_to_invoice.xml
@@ -0,0 +1,16 @@
+
+
+
+
+ WEBSERVICES_3392464_1220201115821392011296470399_67055c545d0
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spec/support/fixtures/attach/attach_file_to_invoice_error.xml b/spec/support/fixtures/attach/attach_file_to_invoice_error.xml
new file mode 100644
index 000000000..4be35e6ef
--- /dev/null
+++ b/spec/support/fixtures/attach/attach_file_to_invoice_error.xml
@@ -0,0 +1,20 @@
+
+
+
+
+ WEBSERVICES_3392464_1220201115821392011296470399_67055c545d0
+
+
+
+
+
+
+
+ INVALID
+ Invalid request.
+
+
+
+
+
+