Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/netsuite.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
87 changes: 87 additions & 0 deletions lib/netsuite/actions/attach_file.rb
Original file line number Diff line number Diff line change
@@ -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

# <soap:Body>
# <platformMsgs:attach>
# <platformCore:attachReference xsi:type="platformCore:AttachContactReference">
# <platformCore::attachTo internalId="176" type="customer" xsi:type="platformCore::RecordRef">
# </platformCore:attachTo>
# <platformCore:attachRecord internalId="1467" type="file" xsi:type="platformCore:RecordRef"/>
# </platformCore:attachReference>
# </platformMsgs:attach>
# </soap:Body>

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
2 changes: 1 addition & 1 deletion lib/netsuite/records/file.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion lib/netsuite/records/invoice.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions lib/netsuite/support/actions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 9 additions & 1 deletion lib/netsuite/support/records.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jdedels I'm not too familiar with the attach_file call, but I believe types should be lower camel case, i.e. customer, salesOrder, transferOrder, etc. Do you know if attach_file uses a different type case or did you just test this with single-word objects?

Thanks again for your help here! Sorry about the delay in taking a look at this

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, it should be lower camel case. I just tested with a sales order and got the following error:

Savon::SOAPFault ((soapenv:Server.userException) org.xml.sax.SAXException: salesorder is not a legal value for {urn:types.core_2020_2.platform.webservices.netsuite.com}RecordType)

Changing the downcase call to lower_camelcase fixed the issue.

Separately, #483 broke this because it introduces a type search-only field on invoices, so when the action calls type to fill out the XML request body, it calls the search-only field, returning nil (unless your record instance happens to be the result of a search), rather than calling this helper.

Broadly I'd guess this is a problem with any instance methods defined on a record colliding with fields on that record. Maybe netsuite_type is better and less chance for collision? Then maybe record_type should be renamed to netsuite_namespaced_type for consistency and clarity?


def record_type_without_namespace
"#{self.class.to_s.split('::').last}"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Yes!! I've been wanting to add this for a while. Thanks!

end

def refresh(credentials = {})
Expand Down
59 changes: 59 additions & 0 deletions spec/netsuite/actions/attach_file_spec.rb
Original file line number Diff line number Diff line change
@@ -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
11 changes: 11 additions & 0 deletions spec/netsuite/records/file_spec.rb
Original file line number Diff line number Diff line change
@@ -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
29 changes: 29 additions & 0 deletions spec/netsuite/records/invoice_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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' }) }
Expand Down
16 changes: 16 additions & 0 deletions spec/support/fixtures/attach/attach_file_to_invoice.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header>
<platformMsgs:documentInfo xmlns:platformMsgs="urn:messages_2011_2.platform.webservices.netsuite.com">
<platformMsgs:nsId>WEBSERVICES_3392464_1220201115821392011296470399_67055c545d0</platformMsgs:nsId>
</platformMsgs:documentInfo>
</soapenv:Header>
<soapenv:Body>
<attachResponse xmlns="urn:messages_2_5.platform.webservices.netsuite.com">
<writeResponse xmlns="urn:messages_2_5.platform.webservices.netsuite.com">
<ns1:status isSuccess="true" xmlns:ns1="urn:core_2_5.platform.webservices.netsuite.com"/>
<baseRef internalId="999" type="invoice" xsi:type="ns2:RecordRef" xmlns:ns2="urn:core_2_5.platform.webservices.netsuite.com"/>
</writeResponse>
</attachResponse>
</soapenv:Body>
</soapenv:Envelope>
20 changes: 20 additions & 0 deletions spec/support/fixtures/attach/attach_file_to_invoice_error.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header>
<platformMsgs:documentInfo xmlns:platformMsgs="urn:messages_2011_2.platform.webservices.netsuite.com">
<platformMsgs:nsId>WEBSERVICES_3392464_1220201115821392011296470399_67055c545d0</platformMsgs:nsId>
</platformMsgs:documentInfo>
</soapenv:Header>
<soapenv:Body>
<attachResponse xmlns="urn:messages_2_5.platform.webservices.netsuite.com">
<writeResponse xmlns="urn:messages_2_5.platform.webservices.netsuite.com">
<platformCore:status xmlns:platformCore="urn:core_2011_2.platform.webservices.netsuite.com" isSuccess="false">
<platformCore:statusDetail type="ERROR">
<platformCore:code>INVALID</platformCore:code>
<platformCore:message>Invalid request.</platformCore:message>
</platformCore:statusDetail>
</platformCore:status>
</writeResponse>
</attachResponse>
</soapenv:Body>
</soapenv:Envelope>