Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge PR#45 to add online invoice support and better support for boolean values #46

Merged
merged 8 commits into from
Jul 3, 2017
16 changes: 15 additions & 1 deletion docs/Attachments.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ This helper contains the following functions:
The `Attachment` object also provides the following method:

* `getContent(writestream)`
* `save(ownerpath, data[, stream])`
* `save(ownerpath, data[, stream, options])`

### Creating a new attachment using a file stream (recommended)

Expand Down Expand Up @@ -59,6 +59,20 @@ Some points to note with the code snippet above:
* The promise that is returned by the `.save()` function contains a response object. This has a bunch of information about the state of the response, but also contains an `entities` array. This array is what actually contains the object that was just created.
* For single object saving, this `entities` array should only ever contain one element, but some objects support a multiple object save and in this case the `entities` array will contain all of the objects that were created.

### Options / IncludeOnline

The options parameter currently only supports one field `IncludeOnline`. This can be used on accounts receivable invoices to choose whether the attachment should or should not be available on the Online Invoice.

This parameter defaults to `false` if not provided.

```javascript
attachmentPlaceholder.save("Invoices/" + invoice.InvoiceID, filePath, false, {IncludeOnline: true})
.then(function(attachments){
//Attachment has been created
var myAttachment = attachments.entities[0];
})
```

### Creating a new attachment using a file reference

This method works, but it is not recommended as it requires the entire attachment to be loaded into memory and passed around. The streaming method is far more efficient.
Expand Down
11 changes: 10 additions & 1 deletion lib/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ Object.assign(Application.prototype, {
if (options.unitdp)
params.unitdp = options.unitdp;

//Added to support attachments POST/PUT for invoices
//being included on the online invoice
if (options.IncludeOnline === true)
params.IncludeOnline = 'true';

var endPointUrl = options.api === 'payroll' ? self.options.payrollAPIEndPointUrl : self.options.coreAPIEndPointUrl;
var url = self.options.baseUrl + endPointUrl + path;
if (!_.isEmpty(params))
Expand Down Expand Up @@ -527,7 +532,11 @@ Object.assign(Application.prototype, {
return new Batch(application);
},
xml2js: function(xml) {
var parser = new xml2js.Parser({ explicitArray: false });
var parser = new xml2js.Parser({
explicitArray: false,
valueProcessors: [xml2js.processors.parseBooleans]
});

return new Promise(function(resolve, reject) {
parser.parseString(xml, function(err, result) {
if (err) return reject(err);
Expand Down
18 changes: 10 additions & 8 deletions lib/entities/accounting/attachment.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ var AttachmentSchema = new Entity.SchemaObject({
FileName: { type: String, toObject: 'always' },
Url: { type: String, toObject: 'always' },
MimeType: { type: String, toObject: 'always' },
ContentLength: { type: Number, toObject: 'always' }
ContentLength: { type: Number, toObject: 'always' },
OnlineInvoice: { type: Boolean, toObject: 'hasValue' }
});


Expand All @@ -21,10 +22,12 @@ var Attachment = Entity.extend(AttachmentSchema, {
getContent: function(writeStream) {
return this.application.core.attachments.getContent(this, writeStream);
},
save: function(ownerPath, data, stream) {
save: function(ownerPath, data, stream, options) {
var self = this;
var path = ownerPath + '/Attachments/' + this.FileName;

options = options || {};

if (!stream) {
//we'll assume data is not a file stream, so we'll create one
data = fs.createReadStream(data);
Expand All @@ -34,12 +37,11 @@ var Attachment = Entity.extend(AttachmentSchema, {
return Promise.reject(new Error("Data must be a valid read stream or file handle."));
}

var options = {
contentType: this.MimeType,
entityPath: 'Attachments.Attachment',
entityConstructor: function(data) {
return self.application.core.attachments.newAttachment(data);
}
//Adding other options for saving purposes
options.contentType = this.MimeType;
options.entityPath = 'Attachments.Attachment';
options.entityConstructor = function(data) {
return self.application.core.attachments.newAttachment(data);
};

return this.application.putOrPostEntity('post', path, data, options);
Expand Down
2 changes: 1 addition & 1 deletion lib/entities/accounting/taxrate.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var TaxComponentSchema = new Entity.SchemaObject({
Name: { type: String, toObject: 'always' },
Rate: { type: String, toObject: 'always' },
//Hacked to string as the framework doesn't recursively translate nested objects
IsCompound: { type: String }
IsCompound: { type: String, toObject: 'hasValue' }
});

var TaxRate = Entity.extend(TaxRateSchema, {
Expand Down
45 changes: 42 additions & 3 deletions test/accountingtests.js
Original file line number Diff line number Diff line change
Expand Up @@ -1281,8 +1281,7 @@ describe('regression tests', function() {
expect(taxComponent.Name).to.not.equal("");
expect(taxComponent.Name).to.not.equal(undefined);
expect(taxComponent.Rate).to.be.a('String');
//Hacked to a string as the framework doesn't recursively translate nested objects
expect(taxComponent.IsCompound).to.be.oneOf(["true", "false"]);
expect(taxComponent.IsCompound).to.be.oneOf([true, false]);
});
});
done();
Expand Down Expand Up @@ -1332,7 +1331,7 @@ describe('regression tests', function() {

//This is hacked toString() because of: https://github.com/jordanwalsh23/xero-node/issues/13
expect(taxComponent.Rate).to.equal(taxrate.TaxComponents[0].Rate.toString());
expect(taxComponent.IsCompound).to.equal(taxrate.TaxComponents[0].IsCompound.toString());
expect(taxComponent.IsCompound).to.equal(taxrate.TaxComponents[0].IsCompound);
});
done();
})
Expand Down Expand Up @@ -2566,6 +2565,46 @@ describe('regression tests', function() {
});
});

it('creates an attachment on an invoice using a file reference and online invoice set to true', function(done) {
var attachmentTemplate = {
FileName: "1-test-attachment.pdf",
MimeType: "application/pdf"
};

var sampleDataReference = __dirname + "/testdata/test-attachment.pdf";

var attachmentPlaceholder = currentApp.core.attachments.newAttachment(attachmentTemplate);

//Add attachment to an Invoice
var filter = 'Type == "ACCREC"';
currentApp.core.invoices.getInvoices({ where: filter })
.then(function(invoices) {
var sampleInvoice = invoices[0];
attachmentPlaceholder.save("Invoices/" + sampleInvoice.InvoiceID, sampleDataReference, false, { IncludeOnline: true })
.then(function(response) {
expect(response.entities.length).to.equal(1);
var thisFile = response.entities[0];
expect(thisFile.AttachmentID).to.not.equal("");
expect(thisFile.AttachmentID).to.not.equal(undefined);
expect(thisFile.FileName).to.equal(attachmentTemplate.FileName);
expect(thisFile.MimeType).to.equal(attachmentTemplate.MimeType);
expect(thisFile.ContentLength).to.be.greaterThan(0);
expect(thisFile.Url).to.not.equal("");
expect(thisFile.Url).to.not.equal(undefined);
expect(thisFile.IncludeOnline).to.equal(true);
done();
})
.catch(function(err) {
console.log(util.inspect(err, null, null));
done(wrapError(err));
})
})
.catch(function(err) {
console.log(util.inspect(err, null, null));
done(wrapError(err));
});
});

it('gets the content of an attachment as stream', function(done) {
//Add attachment to an Invoice
currentApp.core.invoices.getInvoice(invoiceID)
Expand Down