From 9f76387f68ac0279f2556d7c97ec87d57d1255cd Mon Sep 17 00:00:00 2001 From: Seun Martins Date: Thu, 16 Feb 2017 10:44:48 +0100 Subject: [PATCH 001/120] Save company object from Avalara API --- .../client/settings/avalara.html | 13 +++++++----- .../lib/collections/schemas/schema.js | 10 ++++++++++ .../taxes-avalara/server/methods/taxCalc.js | 20 +++++++++---------- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/imports/plugins/included/taxes-avalara/client/settings/avalara.html b/imports/plugins/included/taxes-avalara/client/settings/avalara.html index cad97222e36..1ea06cc1d66 100644 --- a/imports/plugins/included/taxes-avalara/client/settings/avalara.html +++ b/imports/plugins/included/taxes-avalara/client/settings/avalara.html @@ -9,19 +9,22 @@ {{#autoForm collection=Collections.Packages schema=packageConfigSchema doc=packageData type="update" id="avalara-update-form"}}
- {{>afQuickField name='settings.avalara.apiLoginId'}} + {{>afQuickField name='settings.avalara.apiLoginId' class='form-control'}}
- {{>afQuickField name='settings.avalara.username'}} + {{>afQuickField name='settings.avalara.username' class='form-control'}}
- {{>afQuickField name='settings.avalara.password' type='password'}} + {{>afQuickField name='settings.avalara.password' class='form-control' type='password'}}
- {{>afQuickField name='settings.avalara.mode'}} + {{>afQuickField name='settings.avalara.company.companyCode' class='form-control'}}
- {{>afQuickField name='settings.addressValidation.enabled'}} + {{>afQuickField name='settings.avalara.mode' class='form-control'}} +
+
+ {{>afQuickField name='settings.addressValidation.enabled' class='form-control'}}
{{> shopSettingsSubmitButton}} {{/autoForm}} diff --git a/imports/plugins/included/taxes-avalara/lib/collections/schemas/schema.js b/imports/plugins/included/taxes-avalara/lib/collections/schemas/schema.js index e59e3628fdf..c4e2ca3f84f 100644 --- a/imports/plugins/included/taxes-avalara/lib/collections/schemas/schema.js +++ b/imports/plugins/included/taxes-avalara/lib/collections/schemas/schema.js @@ -23,6 +23,16 @@ export const AvalaraPackageConfig = new SimpleSchema([ "settings.avalara.username": { type: String }, + "settings.avalara.company.accountId": { + label: "Company Id", + optional: true, + type: String + }, + "settings.avalara.company.companyCode": { + label: "Company Code", + optional: true, + type: String + }, "settings.avalara.password": { type: String }, diff --git a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js index 27c553b58ec..397400f5d70 100644 --- a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js +++ b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js @@ -78,13 +78,13 @@ taxCalc.getCompanyCode = function () { name: "taxes-avalara", shopId: Reaction.getShopId(), enabled: true - }, { fields: { "settings.avalara.companyCode": 1 } }); - const companyCode = result.settings.avalara.companyCode; + }, { fields: { "settings.avalara.company.companyCode": 1 } }); + const companyCode = _.get(result, "settings.avalara.company.companyCode"); if (companyCode) { return companyCode; } - const savedCompanyCode = taxCalc.saveCompanyCode(); - return savedCompanyCode; + const savedCompany = taxCalc.saveCompany(); + return savedCompany.companyCode; }; /** @@ -163,17 +163,17 @@ taxCalc.getCompanies = function (callback) { }; /** - * @summary Fetch the company code from the API and save in the DB - * @returns {String} Company code + * @summary Fetch the company object from the API and save in the DB + * @returns {String} Company object */ -taxCalc.saveCompanyCode = function () { +taxCalc.saveCompany = function () { const companyData = taxCalc.getCompanies(); - const companyCode = companyData.data.value[0].companyCode; + const company = companyData.data.value[0]; const packageData = taxCalc.getPackageData(); Packages.update({ _id: packageData._id }, { - $set: { "settings.avalara.companyCode": companyCode } + $set: { "settings.avalara.company": company } }); - return companyCode; + return company; }; /** From c354446395e298c870e1bd45774230de4a7f79d6 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Fri, 17 Feb 2017 08:48:13 +0800 Subject: [PATCH 002/120] Load `appVersion` from package,json at startup. Create global `getAppVersion` --- lib/collections/schemas/shops.js | 4 ++++ server/api/core/core.js | 17 +++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/lib/collections/schemas/shops.js b/lib/collections/schemas/shops.js index 262884b262a..b689d0bf8a3 100644 --- a/lib/collections/schemas/shops.js +++ b/lib/collections/schemas/shops.js @@ -238,6 +238,10 @@ export const Shop = new SimpleSchema({ type: [BrandAsset], optional: true }, + "appVersion": { + type: String, + optional: true + }, "createdAt": { type: Date, autoValue: function () { diff --git a/server/api/core/core.js b/server/api/core/core.js index 5216e049cf8..12482570389 100644 --- a/server/api/core/core.js +++ b/server/api/core/core.js @@ -1,4 +1,5 @@ import url from "url"; +import fs from "fs"; import { merge, uniqWith } from "lodash"; import { Meteor } from "meteor/meteor"; import { EJSON } from "meteor/ejson"; @@ -10,6 +11,8 @@ import { registerTemplate } from "./templates"; import { sendVerificationEmail } from "./accounts"; import { getMailUrl } from "./email/config"; + + export default { init() { @@ -30,6 +33,7 @@ export default { this.Import.flush(); // timing is important, packages are rqd for initilial permissions configuration. this.createDefaultAdminUser(); + this.setAppVersion(); // hook after init finished Hooks.Events.run("afterCoreInit"); @@ -207,6 +211,10 @@ export default { return Packages.findOne({ packageName: name, shopId: this.getShopId() }) || null; }, + getAppVersion() { + return Shops.findOne().appVersion; + }, + /** * createDefaultAdminUser * @summary Method that creates default admin user @@ -433,5 +441,14 @@ export default { return false; }); }); + }, + setAppVersion() { + const currentPath = process.env.PWD; + const packagePath = `${currentPath}/package.json`; + const data = fs.readFileSync(packagePath).toString(); + const parsedData = JSON.parse(data); + const version = parsedData.version; + Logger.info(`Reaction Version: ${version}`); + Shops.update({}, { $set: { appVersion: version } }, { multi: true }); } }; From e5c16fa61047cf4e408ef60d02e9a364f3b1fcee Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Fri, 17 Feb 2017 09:04:29 +0800 Subject: [PATCH 003/120] create avaGet function to pass in extra header info. Add getTaxCodes function --- .../taxes-avalara/server/methods/taxCalc.js | 63 ++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js index 397400f5d70..55a866164be 100644 --- a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js +++ b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js @@ -1,4 +1,5 @@ import _ from "lodash"; +import os from "os"; import moment from "moment"; import { Meteor } from "meteor/meteor"; import { HTTP } from "meteor/http"; @@ -52,6 +53,45 @@ function getAuthData() { return auth; } +/** + * @summary function to get HTTP data and pass in extra Avalara-specific headers + * @param {String} requestUrl - The URL to make the request to + * @returns {Object} Response from call + */ +function avaGet(requestUrl) { + const appVersion = Reaction.getAppVersion(); + const machineName = os.hostname(); + const avaClient = `Reaction; ${appVersion}; Meteor HTTP; 1.0; ${machineName}`; + const headers = { + "X-Avalara-Client": avaClient, + "X-Avalara-UID": "xxxxxxx" + }; + const auth = getAuthData(); + const result = HTTP.get(requestUrl, { headers, auth }); + return result; +} + + +/** + * @summary to POST HTTP data and pass in extra Avalara-specific headers + * @param {String} requestUrl - The URL to make the request to + * @param {Object} options - An object of others options, usually data + * @returns {Object} Response from call + */ +function avaPost(requestUrl, options) { + const appVersion = Reaction.getAppVersion(); + const machineName = os.hostname(); + const avaClient = `Reaction; ${appVersion}; Meteor HTTP; 1.0; ${machineName}`; + const headers = { + "X-Avalara-Client": avaClient, + "X-Avalara-UID": "xxxxxxx" + }; + const auth = getAuthData(); + const allOptions = Object.assign({}, options, headers, auth); + const result = HTTP.post(requestUrl, allOptions); + return result; +} + // API Methods /** @@ -87,6 +127,15 @@ taxCalc.getCompanyCode = function () { return savedCompany.companyCode; }; +taxCalc.getCompany = function () { + const result = Packages.findOne({ + name: "taxes-avalara", + shopId: Reaction.getShopId(), + enabled: true + }, { fields: { "settings.avalara.company": 1 } }); + return result; +}; + /** * @summary Validate a particular address * @param {Object} address Address to validate @@ -176,6 +225,17 @@ taxCalc.saveCompany = function () { return company; }; +/** + * @summary get Avalara Tax Codes + * @returns {Array} An array of Tax code objects + */ +taxCalc.getTaxCodes = function () { + const baseUrl = getUrl(); + const requestUrl = `${baseUrl}taxcodes`; + const result = avaGet(requestUrl); + return result.data.value; +}; + /** * @summary Translate RC cart into format for submission * @param {Object} cart RC cart to send for tax estimate @@ -400,5 +460,6 @@ taxCalc.reportRefund = function (order, refundAmount, callback) { export default taxCalc; Meteor.methods({ - "avalara/addressValidation": taxCalc.validateAddress + "avalara/addressValidation": taxCalc.validateAddress, + "avalara/getTaxCodes": taxCalc.getTaxCodes }); From c9e7e8408e202f21b6ed7bae8cf896ef9d98b157 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Fri, 17 Feb 2017 10:22:53 +0800 Subject: [PATCH 004/120] Extend plugin to also provide tax codes --- .../included/taxes-avalara/register.js | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/imports/plugins/included/taxes-avalara/register.js b/imports/plugins/included/taxes-avalara/register.js index fb1d851aaf5..c7d5532fade 100644 --- a/imports/plugins/included/taxes-avalara/register.js +++ b/imports/plugins/included/taxes-avalara/register.js @@ -8,11 +8,16 @@ Reaction.registerPackage({ settings: { avalara: { enabled: false, - apiLoginId: "" + apiLoginId: "", + username: "", + password: "" }, addressValidation: { enabled: false, addressValidationMethod: "avalara/addressValidation" + }, + taxCodes: { + getTaxCodeMethod: "avalara/getTaxCodes" } }, registry: [ @@ -24,8 +29,18 @@ Reaction.registerPackage({ }, { label: "Avalara Address Validation", - name: "addressValidation/avalara", + name: "taxes/addressValidation/avalara", provides: "addressValidation" + }, + { + label: "Avalara Tax Calculation", + provides: "taxMethod", + name: "taxes/calculation/avalara" + }, + { + label: "Avalara Tax Codes", + provides: "taxCodes", + name: "taxes/taxcodes/avalara" } ] }); From 6638741fe83d0364b4d041209628cd8173e057d6 Mon Sep 17 00:00:00 2001 From: Seun Martins Date: Thu, 16 Feb 2017 12:28:27 +0100 Subject: [PATCH 005/120] Add Test connection option in Avalara admin dashboard --- .../taxes-avalara/client/settings/avalara.html | 7 +++++++ .../taxes-avalara/client/settings/avalara.js | 18 ++++++++++++++++++ .../taxes-avalara/server/methods/taxCalc.js | 12 ++++++++---- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/imports/plugins/included/taxes-avalara/client/settings/avalara.html b/imports/plugins/included/taxes-avalara/client/settings/avalara.html index 1ea06cc1d66..80c3e473359 100644 --- a/imports/plugins/included/taxes-avalara/client/settings/avalara.html +++ b/imports/plugins/included/taxes-avalara/client/settings/avalara.html @@ -26,6 +26,13 @@
{{>afQuickField name='settings.addressValidation.enabled' class='form-control'}}
+
{{> shopSettingsSubmitButton}} +
+
+ +
{{/autoForm}} diff --git a/imports/plugins/included/taxes-avalara/client/settings/avalara.js b/imports/plugins/included/taxes-avalara/client/settings/avalara.js index aa7402b50d4..8904e954992 100644 --- a/imports/plugins/included/taxes-avalara/client/settings/avalara.js +++ b/imports/plugins/included/taxes-avalara/client/settings/avalara.js @@ -1,3 +1,4 @@ +import _ from "lodash"; import { Template } from "meteor/templating"; import { AutoForm } from "meteor/aldeed:autoform"; import { Reaction, i18next } from "/client/api"; @@ -17,6 +18,23 @@ Template.avalaraSettings.helpers({ } }); +Template.avalaraSettings.events({ + "click [data-event-action=testConnection]": function (event) { + event.preventDefault(); + event.stopPropagation(); + Meteor.call("avalara/testConnection", function (error, result) { + const statusCode = _.get(result, "statusCode"); + const connectionValid = _.inRange(statusCode, 400); + if (statusCode === 401) { + return Alerts.toast("Connection Test Failed. Save credentials first", "error"); // TODO i18n + } + if (connectionValid) { + return Alerts.toast("Connection Test Success", "success"); // TODO i18n + } + return Alerts.toast("Connection Test Failed", "error"); // TODO i18n + }); + } +}); AutoForm.hooks({ "avalara-update-form": { diff --git a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js index 55a866164be..723893c53e7 100644 --- a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js +++ b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js @@ -43,10 +43,10 @@ function getUrl() { */ function getAuthData() { const packageData = taxCalc.getPackageData(); - const { username, password } = packageData.settings.avalara; + const { username, password } = _.get(packageData, "settings.avalara", {}); if (!username || !password) { - throw new Meteor.Error("You cannot use this API without a username and password configured"); + return new Meteor.Error("You cannot use this API without a username and password configured"); } const auth = `${username}:${password}`; @@ -194,10 +194,13 @@ taxCalc.validateAddress = function (address) { /** * @summary Get all registered companies * @param {Function} callback Callback function for asynchronous execution - * @returns {Object} A list of all companies + * @returns {Object} API response object */ taxCalc.getCompanies = function (callback) { const auth = getAuthData(); + if (auth.error) { + return _.assign({}, auth, { statusCode: 401 }); + } const baseUrl = getUrl(); const requestUrl = `${baseUrl}/companies`; @@ -461,5 +464,6 @@ export default taxCalc; Meteor.methods({ "avalara/addressValidation": taxCalc.validateAddress, - "avalara/getTaxCodes": taxCalc.getTaxCodes + "avalara/getTaxCodes": taxCalc.getTaxCodes, + "avalara/testConnection": taxCalc.getCompanies }); From 91af920b58dd256d71d290463512e98c57ea7bd4 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Fri, 17 Feb 2017 14:30:05 +0800 Subject: [PATCH 006/120] Send taxCode along with cart/orders --- .../taxes-avalara/server/methods/taxCalc.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js index 723893c53e7..b4df08d054c 100644 --- a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js +++ b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js @@ -86,7 +86,7 @@ function avaPost(requestUrl, options) { "X-Avalara-Client": avaClient, "X-Avalara-UID": "xxxxxxx" }; - const auth = getAuthData(); + const auth = { auth: getAuthData() }; const allOptions = Object.assign({}, options, headers, auth); const result = HTTP.post(requestUrl, allOptions); return result; @@ -161,10 +161,9 @@ taxCalc.validateAddress = function (address) { if (address.line2) { addressToValidate.line2 = address.address2; } - const auth = getAuthData(); const baseUrl = getUrl(); const requestUrl = `${baseUrl}/addresses/resolve`; - const result = HTTP.post(requestUrl, { data: addressToValidate, auth: auth }); + const result = avaPost(requestUrl, { data: addressToValidate }); const content = JSON.parse(result.content); if (content.messages) { messages = content.messages; @@ -256,7 +255,8 @@ function cartToSalesOrder(cart) { number: _.toString(index + 1), quantity: item.quantity, amount: item.variants.price * item.quantity, - description: item.title + description: item.title, + taxCode: item.variants.taxCode }; }); } @@ -311,9 +311,8 @@ taxCalc.estimateCart = function (cart, callback) { return callback(data); }); } - const result = HTTP.post(requestUrl, { data: salesOrder, auth: auth }); - const data = JSON.parse(result.content); - return data; + const result = avaPost(requestUrl, { data: salesOrder }); + return result.data; } }; @@ -332,7 +331,8 @@ function orderToSalesInvoice(order) { number: _.toString(index + 1), quantity: item.quantity, amount: item.variants.price * item.quantity, - description: item.title + description: item.title, + taxCode: item.variants.taxCode }; }); From 67e2fd8e9f24e2403134359f0f11f577a19e758b Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Fri, 17 Feb 2017 14:41:17 +0800 Subject: [PATCH 007/120] Capture full tax detail in "taxes" field --- .../taxes-avalara/server/hooks/hooks.js | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/imports/plugins/included/taxes-avalara/server/hooks/hooks.js b/imports/plugins/included/taxes-avalara/server/hooks/hooks.js index d34a8306594..407af004fb3 100644 --- a/imports/plugins/included/taxes-avalara/server/hooks/hooks.js +++ b/imports/plugins/included/taxes-avalara/server/hooks/hooks.js @@ -3,7 +3,23 @@ import { Logger, MethodHooks } from "/server/api"; import { Cart, Orders } from "/lib/collections"; import taxCalc from "../methods/taxCalc"; -MethodHooks.after("taxes/calculate", function (options) { +function linesToTaxes(lines) { + const taxes = lines.map((line) => { + return { + lineNumber: line.lineNumber, + discountAmount: line.discountAmount, + taxable: line.isItemTaxable, + tax: line.tax, + taxableAmount: line.taxableAmount, + taxCode: line.taxCode, + details: line.details + }; + }); + return taxes; +} + + +MethodHooks.after("taxes/calculate", (options) => { const cartId = options.arguments[0]; const cartToCalc = Cart.findOne(cartId); const pkg = taxCalc.getPackageData(); @@ -11,17 +27,19 @@ MethodHooks.after("taxes/calculate", function (options) { Logger.debug("Avalara triggered on taxes/calculate for cartId:", cartId); if (pkg && pkg.settings.avalara.enabled) { taxCalc.estimateCart(cartToCalc, function (result) { + const taxes = linesToTaxes(result.lines); if (result && result.totalTax && typeof result.totalTax === "number") { - const taxAmount = parseFloat(result.totalTax); + // we don't use totalTax, that just tells us we have a valid tax calculation + const taxAmount = taxes.reduce((totalTaxes, tax) => totalTaxes + tax.tax, 0); const taxRate = taxAmount / taxCalc.calcTaxable(cartToCalc); - Meteor.call("taxes/setRate", cartId, taxRate); + Meteor.call("taxes/setRate", cartId, taxRate, taxes); } }); } return options; }); -MethodHooks.after("cart/copyCartToOrder", function (options) { +MethodHooks.after("cart/copyCartToOrder", (options) => { const pkg = taxCalc.getPackageData(); if (pkg && pkg.settings.avalara.enabled) { const cartId = options.arguments[0]; From 053327840a0286c00a19e27c06e5161853807777 Mon Sep 17 00:00:00 2001 From: Seun Martins Date: Mon, 20 Feb 2017 13:55:47 +0100 Subject: [PATCH 008/120] Update Test Credentials to use companyCode API --- .../client/settings/avalara.html | 10 +++---- .../taxes-avalara/client/settings/avalara.js | 12 ++++----- .../lib/collections/schemas/schema.js | 8 ++---- .../taxes-avalara/server/methods/taxCalc.js | 26 +++++++++++++++---- 4 files changed, 34 insertions(+), 22 deletions(-) diff --git a/imports/plugins/included/taxes-avalara/client/settings/avalara.html b/imports/plugins/included/taxes-avalara/client/settings/avalara.html index 80c3e473359..ce71efc5316 100644 --- a/imports/plugins/included/taxes-avalara/client/settings/avalara.html +++ b/imports/plugins/included/taxes-avalara/client/settings/avalara.html @@ -18,7 +18,7 @@ {{>afQuickField name='settings.avalara.password' class='form-control' type='password'}}
- {{>afQuickField name='settings.avalara.company.companyCode' class='form-control'}} + {{>afQuickField name='settings.avalara.companyCode' class='form-control'}}
{{>afQuickField name='settings.avalara.mode' class='form-control'}} @@ -27,12 +27,12 @@ {{>afQuickField name='settings.addressValidation.enabled' class='form-control'}}
- {{> shopSettingsSubmitButton}} +
- + {{> shopSettingsSubmitButton}}
{{/autoForm}} diff --git a/imports/plugins/included/taxes-avalara/client/settings/avalara.js b/imports/plugins/included/taxes-avalara/client/settings/avalara.js index 8904e954992..6ee34e784b4 100644 --- a/imports/plugins/included/taxes-avalara/client/settings/avalara.js +++ b/imports/plugins/included/taxes-avalara/client/settings/avalara.js @@ -19,19 +19,19 @@ Template.avalaraSettings.helpers({ }); Template.avalaraSettings.events({ - "click [data-event-action=testConnection]": function (event) { + "click [data-event-action=testCredentials]": function (event) { event.preventDefault(); event.stopPropagation(); - Meteor.call("avalara/testConnection", function (error, result) { + const formData = AutoForm.getFormValues("avalara-update-form"); + const settings = _.get(formData, "insertDoc.settings.avalara"); + + Meteor.call("avalara/testCredentials", settings, function (error, result) { const statusCode = _.get(result, "statusCode"); const connectionValid = _.inRange(statusCode, 400); - if (statusCode === 401) { - return Alerts.toast("Connection Test Failed. Save credentials first", "error"); // TODO i18n - } if (connectionValid) { return Alerts.toast("Connection Test Success", "success"); // TODO i18n } - return Alerts.toast("Connection Test Failed", "error"); // TODO i18n + return Alerts.toast("Connection Test Failed, Check credentials", "error"); // TODO i18n }); } }); diff --git a/imports/plugins/included/taxes-avalara/lib/collections/schemas/schema.js b/imports/plugins/included/taxes-avalara/lib/collections/schemas/schema.js index c4e2ca3f84f..528d1665fd0 100644 --- a/imports/plugins/included/taxes-avalara/lib/collections/schemas/schema.js +++ b/imports/plugins/included/taxes-avalara/lib/collections/schemas/schema.js @@ -23,14 +23,10 @@ export const AvalaraPackageConfig = new SimpleSchema([ "settings.avalara.username": { type: String }, - "settings.avalara.company.accountId": { - label: "Company Id", - optional: true, + "settings.avalara.companyCode": { type: String }, - "settings.avalara.company.companyCode": { - label: "Company Code", - optional: true, + "settings.avalara.companyId": { type: String }, "settings.avalara.password": { diff --git a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js index b4df08d054c..ef3ab31282e 100644 --- a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js +++ b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js @@ -56,9 +56,10 @@ function getAuthData() { /** * @summary function to get HTTP data and pass in extra Avalara-specific headers * @param {String} requestUrl - The URL to make the request to + * @param {String} authString - (Optional) Combination of username and password. Uses DB values as default * @returns {Object} Response from call */ -function avaGet(requestUrl) { +function avaGet(requestUrl, authString) { const appVersion = Reaction.getAppVersion(); const machineName = os.hostname(); const avaClient = `Reaction; ${appVersion}; Meteor HTTP; 1.0; ${machineName}`; @@ -66,7 +67,7 @@ function avaGet(requestUrl) { "X-Avalara-Client": avaClient, "X-Avalara-UID": "xxxxxxx" }; - const auth = getAuthData(); + const auth = authString || getAuthData(); const result = HTTP.get(requestUrl, { headers, auth }); return result; } @@ -118,8 +119,8 @@ taxCalc.getCompanyCode = function () { name: "taxes-avalara", shopId: Reaction.getShopId(), enabled: true - }, { fields: { "settings.avalara.company.companyCode": 1 } }); - const companyCode = _.get(result, "settings.avalara.company.companyCode"); + }, { fields: { "settings.avalara.companyCode": 1 } }); + const companyCode = _.get(result, "settings.avalara.companyCode"); if (companyCode) { return companyCode; } @@ -213,6 +214,21 @@ taxCalc.getCompanies = function (callback) { } }; +/** + * @summary Tests supplied Avalara credentials by calling company endpoint + * @param {Object} credentials callback Callback function for asynchronous execution + * @returns {Object} Object containing "statusCode" on success, empty response on error + */ +taxCalc.testCredentials = function (credentials) { + check(credentials, Object); + + const baseUrl = getUrl(); + const auth = `${credentials.username}:${credentials.password}`; + const requestUrl = `${baseUrl}/companies/${credentials.companyCode}/transactions`; + const result = avaGet(requestUrl, auth); + return { statusCode: result.statusCode }; +}; + /** * @summary Fetch the company object from the API and save in the DB * @returns {String} Company object @@ -465,5 +481,5 @@ export default taxCalc; Meteor.methods({ "avalara/addressValidation": taxCalc.validateAddress, "avalara/getTaxCodes": taxCalc.getTaxCodes, - "avalara/testConnection": taxCalc.getCompanies + "avalara/testCredentials": taxCalc.testCredentials }); From 4b11f1c6d6f98baf202d3aa173bc6798d79bb300 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Mon, 20 Feb 2017 07:01:39 +0800 Subject: [PATCH 009/120] Add input field for shipping tax code --- .../included/taxes-avalara/client/settings/avalara.html | 3 +++ .../included/taxes-avalara/lib/collections/schemas/schema.js | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/imports/plugins/included/taxes-avalara/client/settings/avalara.html b/imports/plugins/included/taxes-avalara/client/settings/avalara.html index ce71efc5316..8cfa3287dbe 100644 --- a/imports/plugins/included/taxes-avalara/client/settings/avalara.html +++ b/imports/plugins/included/taxes-avalara/client/settings/avalara.html @@ -20,6 +20,9 @@
{{>afQuickField name='settings.avalara.companyCode' class='form-control'}}
+
+ {{>afQuickField name='settings.avalara.shippingTaxCode' class='form-control'}} +
{{>afQuickField name='settings.avalara.mode' class='form-control'}}
diff --git a/imports/plugins/included/taxes-avalara/lib/collections/schemas/schema.js b/imports/plugins/included/taxes-avalara/lib/collections/schemas/schema.js index 528d1665fd0..0561c8d0aeb 100644 --- a/imports/plugins/included/taxes-avalara/lib/collections/schemas/schema.js +++ b/imports/plugins/included/taxes-avalara/lib/collections/schemas/schema.js @@ -37,6 +37,11 @@ export const AvalaraPackageConfig = new SimpleSchema([ type: Boolean, defaultValue: false }, + "settings.avalara.shippingTaxCode": { + label: "Shipping Tax Code", + type: String, + optional: true + }, "settings.addressValidation.enabled": { label: "Address Validation", type: Boolean, From 10ad7868f40effa768a5da1cdf1ac561b4456f2d Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Mon, 20 Feb 2017 07:22:10 +0800 Subject: [PATCH 010/120] Add provides: taxCodes API to taxcloud --- imports/plugins/included/taxes-taxcloud/register.js | 8 ++++++++ .../included/taxes-taxcloud/server/jobs/taxcodes.js | 8 ++++---- .../included/taxes-taxcloud/server/methods/methods.js | 3 ++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/imports/plugins/included/taxes-taxcloud/register.js b/imports/plugins/included/taxes-taxcloud/register.js index 763fb57a73a..82c23046e4a 100644 --- a/imports/plugins/included/taxes-taxcloud/register.js +++ b/imports/plugins/included/taxes-taxcloud/register.js @@ -12,6 +12,9 @@ Reaction.registerPackage({ apiKey: "", refreshPeriod: "every 7 days", taxCodeUrl: "https://taxcloud.net/tic/?format=json" + }, + taxCodes: { + getTaxCodeMethod: "taxcloud/getTaxCodes" } }, registry: [ @@ -20,6 +23,11 @@ Reaction.registerPackage({ name: "taxes/settings/taxcloud", provides: "taxSettings", template: "taxCloudSettings" + }, + { + label: "TaxCloud Tax Codes", + provides: "taxCodes", + name: "taxes/taxcodes/taxcloud" } ] }); diff --git a/imports/plugins/included/taxes-taxcloud/server/jobs/taxcodes.js b/imports/plugins/included/taxes-taxcloud/server/jobs/taxcodes.js index 104901610d0..700da632eb7 100644 --- a/imports/plugins/included/taxes-taxcloud/server/jobs/taxcodes.js +++ b/imports/plugins/included/taxes-taxcloud/server/jobs/taxcodes.js @@ -22,8 +22,8 @@ Hooks.Events.add("afterCoreInit", () => { // set 0 to disable fetchTIC if (refreshPeriod !== 0) { - Logger.debug(`Adding taxes/fetchTIC to JobControl. Refresh ${refreshPeriod}`); - new Job(Jobs, "taxes/fetchTaxCloudTaxCodes", { url: taxCodeUrl }) + Logger.debug(`Adding taxcloud/getTaxCodes to JobControl. Refresh ${refreshPeriod}`); + new Job(Jobs, "taxcloud/getTaxCodes", { url: taxCodeUrl }) .priority("normal") .retry({ retries: 5, @@ -48,13 +48,13 @@ Hooks.Events.add("afterCoreInit", () => { // export default function () { Jobs.processJobs( - "taxes/fetchTaxCloudTaxCodes", + "taxcloud/getTaxCodes", { pollInterval: 30 * 1000, workTimeout: 180 * 1000 }, (job, callback) => { - Meteor.call("taxes/fetchTIC", error => { + Meteor.call("taxcloud/getTaxCodes", error => { if (error) { if (error.error === "notConfigured") { Logger.warn(error.message); diff --git a/imports/plugins/included/taxes-taxcloud/server/methods/methods.js b/imports/plugins/included/taxes-taxcloud/server/methods/methods.js index 714afe829b3..83360344772 100644 --- a/imports/plugins/included/taxes-taxcloud/server/methods/methods.js +++ b/imports/plugins/included/taxes-taxcloud/server/methods/methods.js @@ -5,6 +5,7 @@ import { EJSON } from "meteor/ejson"; import { Logger } from "/server/api"; import Reaction from "../../core/taxes/server/api"; + Meteor.methods({ /** * taxes/fetchTIC @@ -17,7 +18,7 @@ Meteor.methods({ * @param {String} url alternate url to fetch TaxCodes from * @return {undefined} */ - "taxes/fetchTIC": function (url) { + "taxcloud/getTaxCodes": function (url) { check(url, Match.Optional(String)); // check(url, Match.Optional(SimpleSchema.RegEx.Url)); From 4a3dfe1aabf79c29c2e15ff31da1fcbbc1587347 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Mon, 20 Feb 2017 07:49:54 +0800 Subject: [PATCH 011/120] add itemCode to cart/order payload --- .../plugins/included/taxes-avalara/server/methods/taxCalc.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js index ef3ab31282e..a0057a8b61d 100644 --- a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js +++ b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js @@ -269,6 +269,7 @@ function cartToSalesOrder(cart) { lineItems = cart.items.map((item, index) => { return { number: _.toString(index + 1), + itemCode: item._id, quantity: item.quantity, amount: item.variants.price * item.quantity, description: item.title, @@ -345,6 +346,7 @@ function orderToSalesInvoice(order) { const lineItems = order.items.map((item, index) => { return { number: _.toString(index + 1), + itemCode: item._id, quantity: item.quantity, amount: item.variants.price * item.quantity, description: item.title, From f59a3d9947652a628b200d9ca76f96c80cddb242 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Mon, 20 Feb 2017 09:43:18 +0800 Subject: [PATCH 012/120] use item id as line number. Pass in correct productId --- .../included/taxes-avalara/server/methods/taxCalc.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js index a0057a8b61d..2a9a527193a 100644 --- a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js +++ b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js @@ -266,10 +266,10 @@ function cartToSalesOrder(cart) { const currencyCode = company.currency; let lineItems = []; if (cart.items) { - lineItems = cart.items.map((item, index) => { + lineItems = cart.items.map((item) => { return { - number: _.toString(index + 1), - itemCode: item._id, + number: item._id, + itemCode: item.productId, quantity: item.quantity, amount: item.variants.price * item.quantity, description: item.title, @@ -343,10 +343,10 @@ function orderToSalesInvoice(order) { const company = Shops.findOne(Reaction.getShopId()); const companyShipping = _.filter(company.addressBook, (o) => o.isShippingDefault)[0]; const currencyCode = company.currency; - const lineItems = order.items.map((item, index) => { + const lineItems = order.items.map((item) => { return { - number: _.toString(index + 1), - itemCode: item._id, + number: item._id, + itemCode: item.productId, quantity: item.quantity, amount: item.variants.price * item.quantity, description: item.title, From 74fa5766f0d015b16a52c59d03a7b18b493ba2aa Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Mon, 20 Feb 2017 10:50:57 +0800 Subject: [PATCH 013/120] Pass in shipping as additional non-taxable (based on taxcode) item --- .../taxes-avalara/server/methods/taxCalc.js | 24 +++++ lib/collections/collections.js | 15 +-- lib/collections/transform/order.js | 92 +++++++++++++++++++ 3 files changed, 120 insertions(+), 11 deletions(-) create mode 100644 lib/collections/transform/order.js diff --git a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js index 2a9a527193a..c4d0d99120a 100644 --- a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js +++ b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js @@ -264,6 +264,8 @@ function cartToSalesOrder(cart) { const company = Shops.findOne(Reaction.getShopId()); const companyShipping = _.filter(company.addressBook, (o) => o.isShippingDefault)[0]; const currencyCode = company.currency; + const cartShipping = cart.cartShipping(); + const shippingTaxCode = taxCalc.getPackageData().settings.avalara.shippingTaxCode || "NT"; let lineItems = []; if (cart.items) { lineItems = cart.items.map((item) => { @@ -276,6 +278,16 @@ function cartToSalesOrder(cart) { taxCode: item.variants.taxCode }; }); + if (cartShipping) { + lineItems.push({ + number: "shipping", + itemCode: "shipping", + quantity: 1, + amount: cartShipping, + description: "Shipping", + taxCode: shippingTaxCode + }); + } } const salesOrder = { @@ -343,6 +355,8 @@ function orderToSalesInvoice(order) { const company = Shops.findOne(Reaction.getShopId()); const companyShipping = _.filter(company.addressBook, (o) => o.isShippingDefault)[0]; const currencyCode = company.currency; + const orderShipping = order.orderShipping(); + const shippingTaxCode = taxCalc.getPackageData().settings.avalara.shippingTaxCode || "NT"; const lineItems = order.items.map((item) => { return { number: item._id, @@ -353,6 +367,16 @@ function orderToSalesInvoice(order) { taxCode: item.variants.taxCode }; }); + if (orderShipping) { + lineItems.push({ + number: "shipping", + itemCode: "shipping", + quantity: 1, + amount: orderShipping, + description: "Shipping", + taxCode: shippingTaxCode + }); + } const salesInvoice = { companyCode: companyCode, diff --git a/lib/collections/collections.js b/lib/collections/collections.js index c05da348e22..7153f54c17c 100644 --- a/lib/collections/collections.js +++ b/lib/collections/collections.js @@ -1,6 +1,7 @@ import { Mongo } from "meteor/mongo"; import * as Schemas from "./schemas"; import { cartTransform } from "./transform/cart"; +import { orderTransform } from "./transform/order"; /** * @@ -68,17 +69,9 @@ Inventory.attachSchema(Schemas.Inventory); */ export const Orders = new Mongo.Collection("Orders", { transform(order) { - order.itemCount = () => { - let count = 0; - if (order && Array.isArray(order.items)) { - for (const items of order.items) { - count += items.quantity; - } - } - return count; - }; - return order; - } + const newInstance = Object.create(orderTransform); + return _.extend(newInstance, order); + } }); Orders.attachSchema([ diff --git a/lib/collections/transform/order.js b/lib/collections/transform/order.js new file mode 100644 index 00000000000..b4b73083b3f --- /dev/null +++ b/lib/collections/transform/order.js @@ -0,0 +1,92 @@ +import accounting from "accounting-js"; + +/** + * getSummary + * @summary iterates over cart items with computations + * @param {Array} items - order.items array + * @param {Array} prop - path to item property represented by array + * @param {Array} [prop2] - path to another item property represented by array + * @return {Number} - computations result + */ +function getSummary(items, prop, prop2) { + try { + if (Array.isArray(items)) { + return items.reduce((sum, item) => { + if (prop2) { + // S + a * b, where b could be b1 or b2 + return sum + item[prop[0]] * (prop2.length === 1 ? item[prop2[0]] : + item[prop2[0]][prop2[1]]); + } + // S + b, where b could be b1 or b2 + return sum + (prop.length === 1 ? item[prop[0]] : + item[prop[0]][prop[1]]); + }, 0); + } + } catch (e) { + // If data not prepared we should send a number to avoid exception with + // `toFixed`. This could happens if user stuck on `completed` checkout stage + // by some reason. + return 0; + } + return 0; +} + +/** + * Reaction transform collections + * + * transform methods used to return order calculated values + * orderCount, orderSubTotal, orderShipping, orderTaxes, orderTotal + * are calculated by a transformation on the collection + * and are available to use in template as order.xxx + * in template: {{order.orderCount}} + * in code: order.findOne().orderTotal() + */ +export const orderTransform = { + orderCount() { + return getSummary(this.items, ["quantity"]); + }, + orderShipping() { + // loop through the order.shipping, sum shipments. + const rate = getSummary(this.shipping, ["shipmentMethod", "rate"]); + const handling = getSummary(this.shipping, ["shipmentMethod", "handling"]); + const shipping = handling + rate || 0; + return accounting.toFixed(shipping, 2); + }, + orderSubTotal() { + const subTotal = getSummary(this.items, ["quantity"], ["variants", "price"]); + return accounting.toFixed(subTotal, 2); + }, + orderTaxes() { + // taxes are calculated in a order.after.update hooks + // the tax value stored with the order is the effective tax rate + // calculated by line items + // in the imports/core/taxes plugin + const tax = this.tax || 0; + const subTotal = parseFloat(this.orderSubTotal()); + const taxTotal = subTotal * tax; + return accounting.toFixed(taxTotal, 2); + }, + orderDiscounts() { + const discount = this.discount || 0; + return accounting.toFixed(discount, 2); + }, + orderTotal() { + const subTotal = parseFloat(this.orderSubTotal()); + const shipping = parseFloat(this.orderShipping()); + const taxes = parseFloat(this.orderTaxes()); + const discount = parseFloat(this.orderDiscounts()); + const discountTotal = Math.max(0, subTotal - discount); + const total = discountTotal + shipping + taxes; + return accounting.toFixed(total, 2); + }, + itemCount() { + let count = 0; + if (Array.isArray(this.items)) { + for (const item of this.items) { + count += item.quantity; + } + } + return count; + } +}; + From 6c4ed398066d3a8ff78b8be642aa730eb072351d Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Mon, 20 Feb 2017 11:11:11 +0800 Subject: [PATCH 014/120] allow avaPost/avaGet to be used asynchronously --- .../taxes-avalara/server/methods/taxCalc.js | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js index c4d0d99120a..a3bedcced6d 100644 --- a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js +++ b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js @@ -57,9 +57,10 @@ function getAuthData() { * @summary function to get HTTP data and pass in extra Avalara-specific headers * @param {String} requestUrl - The URL to make the request to * @param {String} authString - (Optional) Combination of username and password. Uses DB values as default + * @param {Function} callback - An optional callback for async usage * @returns {Object} Response from call */ -function avaGet(requestUrl, authString) { +function avaGet(requestUrl, callback) { const appVersion = Reaction.getAppVersion(); const machineName = os.hostname(); const avaClient = `Reaction; ${appVersion}; Meteor HTTP; 1.0; ${machineName}`; @@ -67,7 +68,12 @@ function avaGet(requestUrl, authString) { "X-Avalara-Client": avaClient, "X-Avalara-UID": "xxxxxxx" }; - const auth = authString || getAuthData(); + const auth = getAuthData(); + if (callback) { + HTTP.get(requestUrl, { headers, auth }, function (error, result) { + return callback(result); + }); + } const result = HTTP.get(requestUrl, { headers, auth }); return result; } @@ -77,9 +83,10 @@ function avaGet(requestUrl, authString) { * @summary to POST HTTP data and pass in extra Avalara-specific headers * @param {String} requestUrl - The URL to make the request to * @param {Object} options - An object of others options, usually data + * @param {Function} callback - An optional callback for async use * @returns {Object} Response from call */ -function avaPost(requestUrl, options) { +function avaPost(requestUrl, options, callback) { const appVersion = Reaction.getAppVersion(); const machineName = os.hostname(); const avaClient = `Reaction; ${appVersion}; Meteor HTTP; 1.0; ${machineName}`; @@ -89,6 +96,11 @@ function avaPost(requestUrl, options) { }; const auth = { auth: getAuthData() }; const allOptions = Object.assign({}, options, headers, auth); + if (callback) { + HTTP.post(requestUrl, allOptions, function (error, result) { + return callback(result); + }); + } const result = HTTP.post(requestUrl, allOptions); return result; } @@ -331,11 +343,10 @@ taxCalc.estimateCart = function (cart, callback) { if (cart.items && cart.shipping && cart.shipping[0].address) { const salesOrder = cartToSalesOrder(cart); - const auth = getAuthData(); const baseUrl = getUrl(); const requestUrl = `${baseUrl}/transactions/create`; if (callback) { - HTTP.post(requestUrl, { data: salesOrder, auth: auth }, (err, result) => { + avaPost(requestUrl, { data: salesOrder }, (err, result) => { const data = JSON.parse(result.content); return callback(data); }); @@ -416,14 +427,14 @@ function orderToSalesInvoice(order) { */ taxCalc.recordOrder = function (order, callback) { check(callback, Function); + // unlike the other functions, we expect this to always be called asynchronously if (order && order.shipping && order.shipping[0].address) { const salesOrder = orderToSalesInvoice(order); - const auth = getAuthData(); const baseUrl = getUrl(); const requestUrl = `${baseUrl}/transactions/create`; try { - HTTP.post(requestUrl, { data: salesOrder, auth: auth }, (err, result) => { + avaPost(requestUrl, { data: salesOrder }, (err, result) => { if (err) { Logger.error("Encountered error while recording order to Avalara"); Logger.error(err); @@ -435,6 +446,7 @@ taxCalc.recordOrder = function (order, callback) { Logger.error("Encountered error while recording order to Avalara"); Logger.error(error); } + } }; From 1ee2c868205302d776faf9e6179af8b48f2338bd Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Mon, 20 Feb 2017 11:54:23 +0800 Subject: [PATCH 015/120] Add setting to turn off tax calculation separately from disabling the module --- .../included/taxes-avalara/client/settings/avalara.html | 4 ++++ .../taxes-avalara/lib/collections/schemas/schema.js | 5 +++++ .../plugins/included/taxes-avalara/server/hooks/hooks.js | 6 +++--- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/imports/plugins/included/taxes-avalara/client/settings/avalara.html b/imports/plugins/included/taxes-avalara/client/settings/avalara.html index 8cfa3287dbe..5f44c886500 100644 --- a/imports/plugins/included/taxes-avalara/client/settings/avalara.html +++ b/imports/plugins/included/taxes-avalara/client/settings/avalara.html @@ -26,9 +26,13 @@
{{>afQuickField name='settings.avalara.mode' class='form-control'}}
+
+ {{>afQuickField name='settings.avalara.performTaxCalculation' class='form-control'}} +
{{>afQuickField name='settings.addressValidation.enabled' class='form-control'}}
+
- - {{>afFieldInput type="select-multiple" name='settings.addressValidation.countryList' options=countryOptions value=country}} -

Current selection: {{ currentCountryList }}

+ {{> afQuickField name="settings.addressValidation.countryList" type="select-checkbox" options=countryOptions value=country }}
{{>afQuickField name='settings.avalara.enableLogging' class='form-control'}} diff --git a/imports/plugins/included/taxes-avalara/client/settings/avalara.js b/imports/plugins/included/taxes-avalara/client/settings/avalara.js index 224185c4eab..73f8e2e74e6 100644 --- a/imports/plugins/included/taxes-avalara/client/settings/avalara.js +++ b/imports/plugins/included/taxes-avalara/client/settings/avalara.js @@ -18,7 +18,8 @@ Template.avalaraSettings.helpers({ }); }, countryOptions() { - return Countries.find().fetch(); + // Avalara supports only Canada and US for address validation + return Countries.find({ value: { $in: ["US", "CA"] } }).fetch(); }, currentCountryList() { return AutoForm.getFieldValue("settings.addressValidation.countryList"); diff --git a/imports/plugins/included/taxes-avalara/lib/collections/schemas/schema.js b/imports/plugins/included/taxes-avalara/lib/collections/schemas/schema.js index f2c516e507e..4c54090741b 100644 --- a/imports/plugins/included/taxes-avalara/lib/collections/schemas/schema.js +++ b/imports/plugins/included/taxes-avalara/lib/collections/schemas/schema.js @@ -67,7 +67,7 @@ export const AvalaraPackageConfig = new SimpleSchema([ defaultValue: 300 }, "settings.addressValidation.countryList": { - label: "Address Validation Country List", + label: "Enable Address Validation by Country", type: [String], optional: true } From f5cd7a4d4f5ef3a69ed598f4b1001674be579db4 Mon Sep 17 00:00:00 2001 From: Seun Martins Date: Tue, 21 Feb 2017 11:37:45 +0100 Subject: [PATCH 031/120] Fix returned object for non-validated addresses --- .../plugins/included/taxes-avalara/server/methods/taxCalc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js index 0577c39bcb2..3a38ef735f6 100644 --- a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js +++ b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js @@ -158,7 +158,7 @@ taxCalc.validateAddress = function (address) { if (!_.includes(countryList, address.country)) { // if this is a country selected for validation, proceed // else use current address as response - return { validatedAddress: address }; + return { validatedAddress: address, errors: [] }; } let messages; From f9538933540ed8fd261800d6188ead41e87f1968 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Wed, 22 Feb 2017 07:38:34 +0800 Subject: [PATCH 032/120] Log Avalara details to custom Avalogger --- .../taxes-avalara/server/methods/avalogger.js | 35 +++++++++++++++++++ .../taxes-avalara/server/methods/taxCalc.js | 30 +++++++--------- 2 files changed, 47 insertions(+), 18 deletions(-) create mode 100644 imports/plugins/included/taxes-avalara/server/methods/avalogger.js diff --git a/imports/plugins/included/taxes-avalara/server/methods/avalogger.js b/imports/plugins/included/taxes-avalara/server/methods/avalogger.js new file mode 100644 index 00000000000..26f54f957aa --- /dev/null +++ b/imports/plugins/included/taxes-avalara/server/methods/avalogger.js @@ -0,0 +1,35 @@ +import bunyan from "bunyan"; +import bunyanFormat from "bunyan-format"; +import Bunyan2Loggly from "bunyan-loggly"; + +const level = "INFO"; + +// default console config (stdout) +const streams = [{ + level, + stream: bunyanFormat({ outputMode: "short" }) +}]; + +// Loggly config (only used if configured) +const logglyToken = process.env.LOGGLY_TOKEN; +const logglySubdomain = process.env.LOGGLY_SUBDOMAIN; + +if (logglyToken && logglySubdomain) { + const logglyStream = { + type: "raw", + level: "INFO", + stream: new Bunyan2Loggly({ + token: logglyToken, + subdomain: logglySubdomain + }) + }; + streams.push(logglyStream); +} + + +const Avalogger = bunyan.createLogger({ + name: "Avalara", + streams +}); + +export default Avalogger; diff --git a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js index 3a38ef735f6..642179ef992 100644 --- a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js +++ b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js @@ -4,9 +4,10 @@ import os from "os"; import moment from "moment"; import { Meteor } from "meteor/meteor"; import { HTTP } from "meteor/http"; -import { check, Match } from "meteor/check"; +import { check } from "meteor/check"; import { Packages, Shops } from "/lib/collections"; import { Reaction, Logger } from "/server/api"; +import Avalogger from "./avalogger"; const countriesWithRegions = ["US", "CA", "DE", "AU"]; const taxCalc = {}; @@ -59,6 +60,7 @@ function getAuthData(packageData = taxCalc.getPackageData()) { * @returns {Object} Response from call */ function avaGet(requestUrl, options) { + const logObject = {}; const pkgData = taxCalc.getPackageData(); const appVersion = Reaction.getAppVersion(); const meteorVersion = _.split(Meteor.release, "@")[1]; @@ -74,15 +76,14 @@ function avaGet(requestUrl, options) { const timeout = { timeout: options.timeout || pkgData.settings.avalara.requestTimeout }; const allOptions = Object.assign({}, options, headers, { auth }, timeout); if (pkgData.settings.avalara.enableLogging) { - Logger.info("allOptions", allOptions); + logObject.request = allOptions; } const result = HTTP.get(requestUrl, allOptions); if (pkgData.settings.avalara.enableLogging) { - const smallerResult = result; - delete smallerResult.content; - Logger.info("duration", result.headers.serverduration); - Logger.info("result", smallerResult); + logObject.duration = result.headers.serverDuration; + logObject.result = result.data; } + Avalogger.info(logObject); return result; } @@ -94,6 +95,7 @@ function avaGet(requestUrl, options) { * @returns {Object} Response from call */ function avaPost(requestUrl, options) { + const logObject = {}; const pkgData = taxCalc.getPackageData(); const appVersion = Reaction.getAppVersion(); const meteorVersion = _.split(Meteor.release, "@")[1]; @@ -109,21 +111,14 @@ function avaPost(requestUrl, options) { const timeout = { timeout: pkgData.settings.avalara.requestTimeout }; const allOptions = Object.assign({}, options, headers, auth, timeout); if (pkgData.settings.avalara.enableLogging) { - Logger.info("allOptions", allOptions); - if (allOptions.data.lines) { - Logger.info("lines", allOptions.data.lines); - } + logObject.request = allOptions; } const result = HTTP.post(requestUrl, allOptions); if (pkgData.settings.avalara.enableLogging) { - const smallerResult = result; - delete smallerResult.content; - Logger.info("duration", result.headers.serverduration); - Logger.info("result", smallerResult); - if (result.data.lines) { - Logger.info("result lines", result.data.lines); - } + logObject.duration = result.headers.serverDuration; + logObject.result = result.data; } + Avalogger.info(logObject); return result; } @@ -446,7 +441,6 @@ taxCalc.reportRefund = function (order, refundAmount, callback) { const company = Shops.findOne(Reaction.getShopId()); const companyShipping = _.filter(company.addressBook, (o) => o.isShippingDefault)[0]; const currencyCode = company.currency; - const auth = getAuthData(); const baseUrl = getUrl(); const requestUrl = `${baseUrl}/transactions/create`; const returnAmount = refundAmount * -1; From 45a7bc5a32c34eb320bdda6ebd273516420d2424 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Wed, 22 Feb 2017 09:43:19 +0800 Subject: [PATCH 033/120] Set new defaults --- .../taxes-avalara/lib/collections/schemas/schema.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/imports/plugins/included/taxes-avalara/lib/collections/schemas/schema.js b/imports/plugins/included/taxes-avalara/lib/collections/schemas/schema.js index 4c54090741b..be4e1bb3a08 100644 --- a/imports/plugins/included/taxes-avalara/lib/collections/schemas/schema.js +++ b/imports/plugins/included/taxes-avalara/lib/collections/schemas/schema.js @@ -44,7 +44,7 @@ export const AvalaraPackageConfig = new SimpleSchema([ "settings.addressValidation.enabled": { label: "Address Validation", type: Boolean, - defaultValue: false + defaultValue: true }, "settings.avalara.commitDocuments": { label: "Commit Documents", @@ -64,12 +64,13 @@ export const AvalaraPackageConfig = new SimpleSchema([ "settings.avalara.requestTimeout": { label: "Request Timeout", type: Number, - defaultValue: 300 + defaultValue: 1500 }, "settings.addressValidation.countryList": { label: "Enable Address Validation by Country", type: [String], - optional: true + optional: true, + defaultValue: ["US", "CA"] } } ]); From b0269caf1186d2bff7d5c8566cd508dc8d0fcb1f Mon Sep 17 00:00:00 2001 From: Seun Martins Date: Wed, 22 Feb 2017 13:30:31 +0100 Subject: [PATCH 034/120] Add tax settings to account profile page --- .../accounts/templates/profile/profile.html | 4 ++++ .../templates/tax-settings/tax-settings.html | 18 ++++++++++++++++++ .../templates/tax-settings/tax-settings.js | 9 +++++++++ lib/collections/schemas/accounts.js | 15 +++++++++++++++ 4 files changed, 46 insertions(+) create mode 100644 client/modules/accounts/templates/tax-settings/tax-settings.html create mode 100644 client/modules/accounts/templates/tax-settings/tax-settings.js diff --git a/client/modules/accounts/templates/profile/profile.html b/client/modules/accounts/templates/profile/profile.html index eba44c1eb12..f88fdfc7159 100644 --- a/client/modules/accounts/templates/profile/profile.html +++ b/client/modules/accounts/templates/profile/profile.html @@ -41,6 +41,10 @@

Your Orders

{{> addressBookPanel}}
+
+ {{> taxSettingsPanel}} +
+ diff --git a/client/modules/accounts/templates/tax-settings/tax-settings.html b/client/modules/accounts/templates/tax-settings/tax-settings.html new file mode 100644 index 00000000000..e83d36e1075 --- /dev/null +++ b/client/modules/accounts/templates/tax-settings/tax-settings.html @@ -0,0 +1,18 @@ + diff --git a/client/modules/accounts/templates/tax-settings/tax-settings.js b/client/modules/accounts/templates/tax-settings/tax-settings.js new file mode 100644 index 00000000000..24a9d7ceec2 --- /dev/null +++ b/client/modules/accounts/templates/tax-settings/tax-settings.js @@ -0,0 +1,9 @@ +import { Accounts } from "/lib/collections"; +import { Template } from "meteor/templating"; + + +Template.taxSettingsPanel.helpers({ + account() { + return Accounts.findOne(); + } +}); diff --git a/lib/collections/schemas/accounts.js b/lib/collections/schemas/accounts.js index 2dca29213f7..9524f7ab8ee 100644 --- a/lib/collections/schemas/accounts.js +++ b/lib/collections/schemas/accounts.js @@ -7,6 +7,17 @@ import { Metafield } from "./metafield"; * Accounts Schemas */ +const TaxSettings = new SimpleSchema({ + exemptCode: { + type: String, + optional: true + }, + customerUsageType: { + type: String, + optional: true + } +}); + export const Profile = new SimpleSchema({ addressBook: { type: [Address], @@ -75,6 +86,10 @@ export const Accounts = new SimpleSchema({ defaultValue: "new", optional: true }, + taxSettings: { + type: TaxSettings, + optional: true + }, note: { type: String, optional: true From b8660954bf311bfe1f439315860dbcdfafa96902 Mon Sep 17 00:00:00 2001 From: Seun Martins Date: Wed, 22 Feb 2017 18:45:29 +0100 Subject: [PATCH 035/120] Update account find for taxsettings to subscribe --- .../templates/tax-settings/tax-settings.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/client/modules/accounts/templates/tax-settings/tax-settings.js b/client/modules/accounts/templates/tax-settings/tax-settings.js index 24a9d7ceec2..21e42366426 100644 --- a/client/modules/accounts/templates/tax-settings/tax-settings.js +++ b/client/modules/accounts/templates/tax-settings/tax-settings.js @@ -1,9 +1,20 @@ import { Accounts } from "/lib/collections"; import { Template } from "meteor/templating"; - +import { Reaction } from "/client/api"; Template.taxSettingsPanel.helpers({ account() { - return Accounts.findOne(); + if (Reaction.Subscriptions.Account.ready()) { + return Accounts.findOne({ + userId: Meteor.userId() + }); + } + return null; } }); + +Template.taxSettingsPanel.onCreated(function () { + this.autorun(() => { + this.subscribe("Account"); + }); +}); From ce2f8537b94051c073c0e838b36a5f4e6d0e7beb Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Wed, 22 Feb 2017 15:28:26 +0800 Subject: [PATCH 036/120] Only log if logging is enabled --- .../included/taxes-avalara/server/methods/taxCalc.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js index 642179ef992..371ee7fda3e 100644 --- a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js +++ b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js @@ -82,8 +82,9 @@ function avaGet(requestUrl, options) { if (pkgData.settings.avalara.enableLogging) { logObject.duration = result.headers.serverDuration; logObject.result = result.data; + Avalogger.info(logObject); } - Avalogger.info(logObject); + return result; } @@ -117,8 +118,9 @@ function avaPost(requestUrl, options) { if (pkgData.settings.avalara.enableLogging) { logObject.duration = result.headers.serverDuration; logObject.result = result.data; + Avalogger.info(logObject); } - Avalogger.info(logObject); + return result; } From 60c840031329d2852acfd453a78eefab9678d8fe Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Thu, 23 Feb 2017 08:25:34 +0800 Subject: [PATCH 037/120] Write out Avalara requests to Logs collection via Bunyan logger --- .../taxes-avalara/server/methods/avalogger.js | 33 ++++++++----------- lib/collections/collections.js | 8 +++++ lib/collections/schemas/index.js | 1 + lib/collections/schemas/logs.js | 16 +++++++++ 4 files changed, 38 insertions(+), 20 deletions(-) create mode 100644 lib/collections/schemas/logs.js diff --git a/imports/plugins/included/taxes-avalara/server/methods/avalogger.js b/imports/plugins/included/taxes-avalara/server/methods/avalogger.js index 26f54f957aa..5304fe0e724 100644 --- a/imports/plugins/included/taxes-avalara/server/methods/avalogger.js +++ b/imports/plugins/included/taxes-avalara/server/methods/avalogger.js @@ -1,33 +1,26 @@ import bunyan from "bunyan"; -import bunyanFormat from "bunyan-format"; -import Bunyan2Loggly from "bunyan-loggly"; +import { Logs } from "/lib/collections"; const level = "INFO"; -// default console config (stdout) -const streams = [{ - level, - stream: bunyanFormat({ outputMode: "short" }) -}]; +class BunyanMongo {} + -// Loggly config (only used if configured) -const logglyToken = process.env.LOGGLY_TOKEN; -const logglySubdomain = process.env.LOGGLY_SUBDOMAIN; +BunyanMongo.prototype.write = Meteor.bindEnvironment((logData) => { + const avalog = { logType: "avalara", data: logData }; + Logs.insert(avalog); +}); -if (logglyToken && logglySubdomain) { - const logglyStream = { +const streams = [ + { type: "raw", - level: "INFO", - stream: new Bunyan2Loggly({ - token: logglyToken, - subdomain: logglySubdomain - }) - }; - streams.push(logglyStream); -} + stream: new BunyanMongo() + } +]; const Avalogger = bunyan.createLogger({ + level, name: "Avalara", streams }); diff --git a/lib/collections/collections.js b/lib/collections/collections.js index 7153f54c17c..54ec1b3353e 100644 --- a/lib/collections/collections.js +++ b/lib/collections/collections.js @@ -165,3 +165,11 @@ Notifications.attachSchema(Schemas.Notification); export const Sms = new Mongo.Collection("Sms"); Sms.attachSchema(Schemas.Sms); + + +/** + * Logs Collection + */ +export const Logs = new Mongo.Collection("Logs"); + +Logs.attachSchema(Schemas.Logs); diff --git a/lib/collections/schemas/index.js b/lib/collections/schemas/index.js index 2c73c562611..0419b283069 100644 --- a/lib/collections/schemas/index.js +++ b/lib/collections/schemas/index.js @@ -6,6 +6,7 @@ export * from "./cart"; export * from "./emails"; export * from "./inventory"; export * from "./layouts"; +export * from "./logs"; export * from "./metafield"; export * from "./notifications"; export * from "./orders"; diff --git a/lib/collections/schemas/logs.js b/lib/collections/schemas/logs.js new file mode 100644 index 00000000000..0250f5a3e55 --- /dev/null +++ b/lib/collections/schemas/logs.js @@ -0,0 +1,16 @@ +import { SimpleSchema } from "meteor/aldeed:simple-schema"; + +export const Logs = new SimpleSchema({ + + logType: { + type: String + }, + data: { + type: Object, + blackbox: true + }, + date: { + type: Date, + autoValue() { return new Date(); } + } +}); From 071edbcaa7577ac77db256f770db311b36ce6e3e Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Thu, 23 Feb 2017 08:31:59 +0800 Subject: [PATCH 038/120] Add log retention duration setting --- .../included/taxes-avalara/client/settings/avalara.html | 3 +++ .../included/taxes-avalara/lib/collections/schemas/schema.js | 5 +++++ imports/plugins/included/taxes-avalara/register.js | 3 ++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/imports/plugins/included/taxes-avalara/client/settings/avalara.html b/imports/plugins/included/taxes-avalara/client/settings/avalara.html index 0caf9527c97..5af5ebde7cc 100644 --- a/imports/plugins/included/taxes-avalara/client/settings/avalara.html +++ b/imports/plugins/included/taxes-avalara/client/settings/avalara.html @@ -38,6 +38,9 @@
{{>afQuickField name='settings.avalara.enableLogging' class='form-control'}}
+
+ {{>afQuickField name='settings.avalara.logRetentionDuration' class='form-control'}} +
{{>afQuickField name='settings.avalara.requestTimeout' class='form-control'}}
diff --git a/imports/plugins/included/taxes-avalara/lib/collections/schemas/schema.js b/imports/plugins/included/taxes-avalara/lib/collections/schemas/schema.js index be4e1bb3a08..233f13059c5 100644 --- a/imports/plugins/included/taxes-avalara/lib/collections/schemas/schema.js +++ b/imports/plugins/included/taxes-avalara/lib/collections/schemas/schema.js @@ -61,6 +61,11 @@ export const AvalaraPackageConfig = new SimpleSchema([ type: Boolean, defaultValue: false }, + "settings.avalara.logRetentionDuration": { + label: "Retain Logs Duration (Days)", + type: Number, + defaultValue: 30 + }, "settings.avalara.requestTimeout": { label: "Request Timeout", type: Number, diff --git a/imports/plugins/included/taxes-avalara/register.js b/imports/plugins/included/taxes-avalara/register.js index 0d4f08a1d17..8a34b8c8196 100644 --- a/imports/plugins/included/taxes-avalara/register.js +++ b/imports/plugins/included/taxes-avalara/register.js @@ -15,7 +15,8 @@ Reaction.registerPackage({ commitDocuments: true, performTaxCalculation: true, enableLogging: false, - requestTimeout: 300 + requestTimeout: 300, + logRetentionDuration: 30 }, addressValidation: { enabled: true, From cd55f91c5c014fd4b879f3764f869a9b49c1cb35 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Thu, 23 Feb 2017 10:15:30 +0800 Subject: [PATCH 039/120] Add jobs to cleanup logs older than configured setting --- .../included/taxes-avalara/server/index.js | 4 ++ .../taxes-avalara/server/jobs/cleanup.js | 68 +++++++++++++++++++ .../taxes-avalara/server/methods/avalogger.js | 3 +- lib/collections/schemas/logs.js | 3 + 4 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 imports/plugins/included/taxes-avalara/server/jobs/cleanup.js diff --git a/imports/plugins/included/taxes-avalara/server/index.js b/imports/plugins/included/taxes-avalara/server/index.js index 68dce60357a..a866f0d3fbe 100644 --- a/imports/plugins/included/taxes-avalara/server/index.js +++ b/imports/plugins/included/taxes-avalara/server/index.js @@ -1,2 +1,6 @@ import "./hooks"; import "./i18n"; +import "./jobs/cleanup"; +import cleanupAvalogs from "./jobs/cleanup"; + +cleanupAvalogs(); diff --git a/imports/plugins/included/taxes-avalara/server/jobs/cleanup.js b/imports/plugins/included/taxes-avalara/server/jobs/cleanup.js new file mode 100644 index 00000000000..f773f0e3c22 --- /dev/null +++ b/imports/plugins/included/taxes-avalara/server/jobs/cleanup.js @@ -0,0 +1,68 @@ +import moment from "moment"; +import { Meteor } from "meteor/meteor"; +import { Jobs, Logs } from "/lib/collections"; +import { Hooks, Logger } from "/server/api"; +import taxCalc from "../methods/taxCalc"; + + +/** + * @summary Remove logs older than the configured number of days + * @returns results of remmoval query + */ +function cleanupAvalaraJobs(callback) { + const pkgData = taxCalc.getPackageData(); + if (pkgData && pkgData.settings.avalara.enabled) { + const saveDuration = pkgData.settings.avalara.logRetentionDuration; + const olderThan = moment().subtract(saveDuration, "days"); + const result = Logs.remove({ + date: { + $lt: olderThan + } + }); + Logger.debug(`Removed ${result} Avalara log records`); + } + callback(); +} + + +Hooks.Events.add("afterCoreInit", () => { + if (!Meteor.isAppTest) { + Logger.debug("Adding Avalara log cleanup job and removing existing"); + // Renove all previous jobs + Jobs.remove({ type: "logs/removeOldAvalaraLogs" }); + new Job(Jobs, "logs/removeOldAvalaraLogs", {}) + .priority("normal") + .retry({ + retries: 5, + wait: 60000, + backoff: "exponential" + }) + .save({ + cancelRepeats: true + }); + } +}); + + +export default function () { + Jobs.processJobs("logs/removeOldAvalaraLogs", + { + pollInterval: 30 * 1000, + workTimeout: 180 * 1000 + }, + (job, callback) => { + Logger.debug("Avalara log cleanup running"); + cleanupAvalaraJobs(function (error) { + if (error) { + job.done(error.toString(), { repeatId: true }); + callback(); + } else { + const success = "Avalara Log Cleanup ran successfully"; + Logger.debug(success); + job.done(success, { repeatId: true }); + callback(); + } + }); + } + ); +} diff --git a/imports/plugins/included/taxes-avalara/server/methods/avalogger.js b/imports/plugins/included/taxes-avalara/server/methods/avalogger.js index 5304fe0e724..0daf10df340 100644 --- a/imports/plugins/included/taxes-avalara/server/methods/avalogger.js +++ b/imports/plugins/included/taxes-avalara/server/methods/avalogger.js @@ -1,5 +1,6 @@ import bunyan from "bunyan"; import { Logs } from "/lib/collections"; +import { Reaction } from "/server/api"; const level = "INFO"; @@ -7,7 +8,7 @@ class BunyanMongo {} BunyanMongo.prototype.write = Meteor.bindEnvironment((logData) => { - const avalog = { logType: "avalara", data: logData }; + const avalog = { logType: "avalara", shopId: Reaction.getShopId(), data: logData }; Logs.insert(avalog); }); diff --git a/lib/collections/schemas/logs.js b/lib/collections/schemas/logs.js index 0250f5a3e55..e5469194b17 100644 --- a/lib/collections/schemas/logs.js +++ b/lib/collections/schemas/logs.js @@ -5,6 +5,9 @@ export const Logs = new SimpleSchema({ logType: { type: String }, + shopId: { + type: String + }, data: { type: Object, blackbox: true From 64366fdfa311df25f3830bcbba90cdeaeb141828 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Thu, 23 Feb 2017 14:17:45 +0800 Subject: [PATCH 040/120] Add logs subscription that totally doesn't work --- .../taxes-avalara/client/settings/avalara.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/imports/plugins/included/taxes-avalara/client/settings/avalara.js b/imports/plugins/included/taxes-avalara/client/settings/avalara.js index 73f8e2e74e6..c9f6b1ecb52 100644 --- a/imports/plugins/included/taxes-avalara/client/settings/avalara.js +++ b/imports/plugins/included/taxes-avalara/client/settings/avalara.js @@ -1,12 +1,23 @@ import _ from "lodash"; import { Template } from "meteor/templating"; +import { Tracker } from "meteor/tracker"; +import { Meteor } from "meteor/meteor"; import { AutoForm } from "meteor/aldeed:autoform"; import { Countries } from "/client/collections"; import { Reaction, i18next } from "/client/api"; -import { Packages } from "/lib/collections"; +import { Packages, Logs } from "/lib/collections"; import { AvalaraPackageConfig } from "../../lib/collections/schemas"; +Template.avalaraSettings.onCreated(function () { + const handle = Meteor.subscribe("Logs"); + Tracker.autorun(() => { + const isReady = handle.ready(); + console.log(`Handle is ${isReady ? "ready" : "not ready"}`); + }); +}); + + Template.avalaraSettings.helpers({ packageConfigSchema() { return AvalaraPackageConfig; From 1c8aeaf0b83274cc0bea4710957f3c12a6f4eecd Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Thu, 23 Feb 2017 16:35:48 +0800 Subject: [PATCH 041/120] Add logs publication --- imports/plugins/core/logging/register.js | 7 +++++ imports/plugins/core/logging/server/index.js | 1 + .../core/logging/server/publications.js | 26 +++++++++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 imports/plugins/core/logging/register.js create mode 100644 imports/plugins/core/logging/server/index.js create mode 100644 imports/plugins/core/logging/server/publications.js diff --git a/imports/plugins/core/logging/register.js b/imports/plugins/core/logging/register.js new file mode 100644 index 00000000000..5db8069f208 --- /dev/null +++ b/imports/plugins/core/logging/register.js @@ -0,0 +1,7 @@ +import { Reaction } from "/server/api"; + +Reaction.registerPackage({ + label: "Logging", + name: "reaction-logging", + autoEnable: true +}); diff --git a/imports/plugins/core/logging/server/index.js b/imports/plugins/core/logging/server/index.js new file mode 100644 index 00000000000..a608e2019a2 --- /dev/null +++ b/imports/plugins/core/logging/server/index.js @@ -0,0 +1 @@ +import "./publications"; diff --git a/imports/plugins/core/logging/server/publications.js b/imports/plugins/core/logging/server/publications.js new file mode 100644 index 00000000000..560720a55bd --- /dev/null +++ b/imports/plugins/core/logging/server/publications.js @@ -0,0 +1,26 @@ +import { Meteor } from "meteor/meteor"; +import { check } from "meteor/check"; +import { Roles } from "meteor/alanning:roles"; +import { Counts } from "meteor/tmeasday:publish-counts"; +import { Logs } from "/lib/collections"; +import { Reaction } from "/server/api"; + + +/** + * Publish logs + * Poor admins get swamped with a ton of data so let's just only subscribe to one + * logType at a time + */ +Meteor.publish("Logs", function (logType) { + check(logType, String); + + const shopId = Reaction.getShopId(); + if (!shopId) { + return this.ready(); + } + + if (Roles.userIsInRole(this.userId, ["admin", "owner"])) { + Counts.publish(this, "logs-count", Logs.find({shopId, logType})); + return Logs.find({shopId, logType}); + } +}); From c098fceff8eed863c47233d6829ffe5b63d9fdf5 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Thu, 23 Feb 2017 17:12:46 +0800 Subject: [PATCH 042/120] Get logs and pass into Template --- .../client/settings/avalara.html | 8 ++++++ .../taxes-avalara/client/settings/avalara.js | 26 ++++++++++++++----- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/imports/plugins/included/taxes-avalara/client/settings/avalara.html b/imports/plugins/included/taxes-avalara/client/settings/avalara.html index 5af5ebde7cc..ced611e755c 100644 --- a/imports/plugins/included/taxes-avalara/client/settings/avalara.html +++ b/imports/plugins/included/taxes-avalara/client/settings/avalara.html @@ -47,10 +47,18 @@
{{>afQuickField name='settings.avalara.commitDocuments' class='form-control'}}
+ {{#if loggingEnabled}} +
+ +
+ {{/if}} +
{{/autoForm}} + + {{#if loggingEnabled}} +
+
+ {{> React logGrid }} +
+
+ {{#if instance.state.get 'isEditing'}} + {{#if instance.state.get 'editingId'}} +
+ {{#autoForm + collection=logCollection + schema=logSchema + id="log-view-form" + template="bootstrap3-inline" + doc=logEntry + }} + +
+
+
+ {{>afQuickField name="date" class="form-control" disabled=true}} + {{>afQuickField name="data" class="form-control" type="textarea" rows="15"}} +
+
+
+ + {{/autoForm}} +
+ + {{/if}} + {{/if}} + {{/if}} + + diff --git a/imports/plugins/included/taxes-avalara/client/settings/avalara.js b/imports/plugins/included/taxes-avalara/client/settings/avalara.js index 5ab78d41c02..281c226a247 100644 --- a/imports/plugins/included/taxes-avalara/client/settings/avalara.js +++ b/imports/plugins/included/taxes-avalara/client/settings/avalara.js @@ -1,12 +1,16 @@ import _ from "lodash"; import { Template } from "meteor/templating"; -import { Tracker } from "meteor/tracker"; +import { ReactiveDict } from "meteor/reactive-dict"; import { Meteor } from "meteor/meteor"; import { AutoForm } from "meteor/aldeed:autoform"; import { Countries } from "/client/collections"; import { Reaction, i18next } from "/client/api"; import { Packages, Logs } from "/lib/collections"; +import { Logs as LogSchema } from "/lib/collections/schemas/logs"; import { AvalaraPackageConfig } from "../../lib/collections/schemas"; +import LogGriddle from "./avagriddle"; +import { Loading } from "/imports/plugins/core/ui/client/components"; + function getPackageData() { return Packages.findOne({ @@ -17,11 +21,16 @@ function getPackageData() { Template.avalaraSettings.onCreated(function () { - const logSub = Meteor.subscribe("Logs", "avalara"); - Tracker.autorun(() => { - if (logSub.ready()) { - this.logs = Logs.find({}); - } + this.autorun(() => { + this.subscribe("Logs", { + logType: "avalara" + }); + }); + + this.state = new ReactiveDict(); + this.state.setDefault({ + isEditing: false, + editingId: null }); }); @@ -33,6 +42,12 @@ Template.avalaraSettings.helpers({ packageData() { return getPackageData(); }, + logSchema() { + return LogSchema; + }, + logCollection() { + return Logs; + }, countryOptions() { // Avalara supports only Canada and US for address validation return Countries.find({ value: { $in: ["US", "CA"] } }).fetch(); @@ -44,12 +59,83 @@ Template.avalaraSettings.helpers({ const pkgData = getPackageData(); return pkgData.settings.avalara.enableLogging; }, - avalogs() { - return Logs.find({}).fetch(); + + logGrid() { + const fields = ["date", "request", "result", "_id"]; + const noDataMessage = i18next.t("logGrid.noLogsFound"); + const instance = Template.instance(); + + // + // helper to get and select row from griddle + // into blaze to get correct template to edit + // + function editRow(options) { + const currentId = instance.state.get("editingId"); + instance.state.set("isEditing", options.props.data); + instance.state.set("editingId", options.props.data._id); + // toggle edit mode clicking on same row + if (currentId === options.props.data._id) { + instance.state.set("isEditing", null); + instance.state.set("editingId", null); + } + } + + // helper adds a class to every grid row + const customRowMetaData = { + bodyCssClassName: () => { + return "log-grid-row"; + } + }; + + // add i18n handling to headers + const customColumnMetadata = []; + fields.forEach(function (field) { + const columnMeta = { + columnName: field, + displayName: i18next.t(`logGrid.columns.${field}`) + }; + customColumnMetadata.push(columnMeta); + }); + + // return template Grid + return { + component: LogGriddle, + publication: "Logs", + collection: Logs, + matchingResultsCount: "logs-count", + useGriddleStyles: false, + rowMetadata: customRowMetaData, + columns: fields, + noDataMessage: noDataMessage, + onRowClick: editRow, + columnMetadata: customColumnMetadata, + externalLoadingComponent: Loading, + subscriptionParams: { logType: "avalara" } + }; + }, + + instance() { + const instance = Template.instance(); + return instance; + }, + + logEntry() { + const instance = Template.instance(); + const id = instance.state.get("editingId"); + const log = Logs.findOne(id) || {}; + log.data = JSON.stringify(log.data, null, 4); + return log; } + + }); Template.avalaraSettings.events({ + "click .template-grid-row": function (event) { + // toggle all rows off, then add our active row + $(".template-grid-row").removeClass("active"); + Template.instance().$(event.currentTarget).addClass("active"); + }, "click [data-event-action=testCredentials]": function (event) { event.preventDefault(); event.stopPropagation(); From 9d5b21b884d080891a62fd7aa79194d9b38edfd0 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Fri, 24 Feb 2017 17:56:42 +0800 Subject: [PATCH 045/120] Refine grid and detail results --- .../included/taxes-avalara/client/settings/avagriddle.js | 7 ++++--- .../included/taxes-avalara/client/settings/avalara.html | 8 +++++--- .../included/taxes-avalara/client/settings/avalara.js | 6 +----- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/imports/plugins/included/taxes-avalara/client/settings/avagriddle.js b/imports/plugins/included/taxes-avalara/client/settings/avagriddle.js index 29d1dda8c99..81a7a7effbc 100644 --- a/imports/plugins/included/taxes-avalara/client/settings/avagriddle.js +++ b/imports/plugins/included/taxes-avalara/client/settings/avagriddle.js @@ -30,9 +30,10 @@ const LogGriddle = React.createClass({ const rawResults = this.props.collection.find({}).fetch(); const results = rawResults.map((o) => { return { - date: moment(o.data).format("MM/DD/YYYY hh:mm:ss"), - request: JSON.stringify(o.data.request).substring(0, 10), - result: JSON.stringify(o.data.result).substring(0, 10), + date: moment(o.data).format("MM/DD/YYYY HH:mm:ss"), + docType: o.data.request.data.type, + request: JSON.stringify(o.data.request), + result: JSON.stringify(o.data.result), _id: o._id } }); diff --git a/imports/plugins/included/taxes-avalara/client/settings/avalara.html b/imports/plugins/included/taxes-avalara/client/settings/avalara.html index 4ee4b4d0011..661e536dba8 100644 --- a/imports/plugins/included/taxes-avalara/client/settings/avalara.html +++ b/imports/plugins/included/taxes-avalara/client/settings/avalara.html @@ -63,9 +63,11 @@ {{/autoForm}} {{#if loggingEnabled}} -
-
- {{> React logGrid }} +
+
+
+ {{> React logGrid }} +
{{#if instance.state.get 'isEditing'}} diff --git a/imports/plugins/included/taxes-avalara/client/settings/avalara.js b/imports/plugins/included/taxes-avalara/client/settings/avalara.js index 281c226a247..de801230e94 100644 --- a/imports/plugins/included/taxes-avalara/client/settings/avalara.js +++ b/imports/plugins/included/taxes-avalara/client/settings/avalara.js @@ -61,14 +61,10 @@ Template.avalaraSettings.helpers({ }, logGrid() { - const fields = ["date", "request", "result", "_id"]; + const fields = ["date", "docType"]; const noDataMessage = i18next.t("logGrid.noLogsFound"); const instance = Template.instance(); - // - // helper to get and select row from griddle - // into blaze to get correct template to edit - // function editRow(options) { const currentId = instance.state.get("editingId"); instance.state.set("isEditing", options.props.data); From 26ba8034f5d64549f789546503ebf9fc75ce17c3 Mon Sep 17 00:00:00 2001 From: Seun Martins Date: Fri, 24 Feb 2017 16:31:07 +0100 Subject: [PATCH 046/120] Populate entity codes and plug into taxCalc --- client/collections/index.js | 1 + client/collections/tax-entitycodes.js | 6 +++ .../templates/dashboard/dashboard.html | 9 ++++ .../accounts/templates/profile/profile.html | 4 -- .../templates/tax-settings/tax-settings.html | 12 +++-- .../templates/tax-settings/tax-settings.js | 52 +++++++++++++++++++ .../taxes-avalara/server/methods/taxCalc.js | 30 +++++++++-- lib/collections/schemas/accounts.js | 2 +- private/data/i18n/en.json | 3 +- 9 files changed, 105 insertions(+), 14 deletions(-) create mode 100644 client/collections/tax-entitycodes.js diff --git a/client/collections/index.js b/client/collections/index.js index fed7f1632cd..ed6a02c7162 100644 --- a/client/collections/index.js +++ b/client/collections/index.js @@ -1 +1,2 @@ export * from "./countries"; +export * from "./tax-entitycodes"; diff --git a/client/collections/tax-entitycodes.js b/client/collections/tax-entitycodes.js new file mode 100644 index 00000000000..d73541dfc11 --- /dev/null +++ b/client/collections/tax-entitycodes.js @@ -0,0 +1,6 @@ +import { Mongo } from "meteor/mongo"; + +/** + * Client side collections + */ +export const TaxEntityCodes = new Mongo.Collection(null); diff --git a/client/modules/accounts/templates/dashboard/dashboard.html b/client/modules/accounts/templates/dashboard/dashboard.html index d055c0fa5d3..ef74c770c4c 100644 --- a/client/modules/accounts/templates/dashboard/dashboard.html +++ b/client/modules/accounts/templates/dashboard/dashboard.html @@ -40,6 +40,15 @@

+
+
+

+ Tax Exempt Settings +

+
+ {{> taxSettingsPanel}} +
+
{{/if}} diff --git a/client/modules/accounts/templates/profile/profile.html b/client/modules/accounts/templates/profile/profile.html index f88fdfc7159..eba44c1eb12 100644 --- a/client/modules/accounts/templates/profile/profile.html +++ b/client/modules/accounts/templates/profile/profile.html @@ -41,10 +41,6 @@

Your Orders

{{> addressBookPanel}} -
- {{> taxSettingsPanel}} -
- diff --git a/client/modules/accounts/templates/tax-settings/tax-settings.html b/client/modules/accounts/templates/tax-settings/tax-settings.html index e83d36e1075..67a0b356cc9 100644 --- a/client/modules/accounts/templates/tax-settings/tax-settings.html +++ b/client/modules/accounts/templates/tax-settings/tax-settings.html @@ -1,13 +1,17 @@ - - From 27f825d5e05987cc3081d8e1cccebe3fa5833733 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Mon, 27 Feb 2017 10:00:48 +0800 Subject: [PATCH 051/120] Add translations --- .../taxes-avalara/server/i18n/en.json | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/imports/plugins/included/taxes-avalara/server/i18n/en.json b/imports/plugins/included/taxes-avalara/server/i18n/en.json index 79d887bd66b..82927b4c4dc 100644 --- a/imports/plugins/included/taxes-avalara/server/i18n/en.json +++ b/imports/plugins/included/taxes-avalara/server/i18n/en.json @@ -4,6 +4,29 @@ "ns": "taxes-avalara", "translation": { "reaction-taxes": { + "settings": { + "apiLoginId": "Avalara API Login ID", + "username": "Username", + "password": "Password", + "companyCode": "Company Code", + "shippingTaxCode": "Shipping Tax Code", + "mode": "Production Mode", + "performTaxCalculation": "Perform Tax Calculation", + "addressValidation": "Address Validation", + "enableAddressValidationByCountry": "Enable Address Validation by Country", + "enableLogging": "Enable Logging", + "logRetentionDuration": "Log Retention Duration", + "requestTimeout": "Request Timeout", + "commitDocuments": "Commit Documents", + "administratorsPanel": "Administrator's Panel" + }, + "logGrid": { + "noLogsFound": "No logs found", + "columns": { + "date": "Date", + "docType": "Document Type" + } + }, "admin": { "shortcut": { "avalaraLabel": "Avalara", @@ -12,13 +35,12 @@ "dashboard": { "avalaraLabel": "Avalara", "avalaraTitle": "Avalara", - "avalaraDescription": "Avalara Tax Rates" + "avalaraDescription": "Avalara Tax Rates", + "testCredentials": "Test Credentials" }, "taxSettings": { "avalaraLabel": "Avalara", - "avalaraSettingsLabel": "Avalara", - "avalaraCredentials": "Add credentials to enable", - "avalaraGetCredentialsURL": "Get them here" + "avalaraSettingsLabel": "Avalara" } } } From 0ef28176e6b17ec949f6953ad636b7f6fd231c57 Mon Sep 17 00:00:00 2001 From: Seun Martins Date: Mon, 27 Feb 2017 14:03:46 +0100 Subject: [PATCH 052/120] Show tax settings only if avalara enabled --- .../accounts/templates/dashboard/dashboard.html | 2 ++ .../accounts/templates/dashboard/dashboard.js | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/client/modules/accounts/templates/dashboard/dashboard.html b/client/modules/accounts/templates/dashboard/dashboard.html index ef74c770c4c..6da5107fe89 100644 --- a/client/modules/accounts/templates/dashboard/dashboard.html +++ b/client/modules/accounts/templates/dashboard/dashboard.html @@ -40,6 +40,7 @@

+ {{#if showAvalaraTaxSettings }}

@@ -48,6 +49,7 @@

{{> taxSettingsPanel}}
+ {{/if}} {{/if}} diff --git a/client/modules/accounts/templates/dashboard/dashboard.js b/client/modules/accounts/templates/dashboard/dashboard.js index 36d236233d1..b7b69eb96da 100644 --- a/client/modules/accounts/templates/dashboard/dashboard.js +++ b/client/modules/accounts/templates/dashboard/dashboard.js @@ -3,6 +3,7 @@ import { Meteor } from "meteor/meteor"; import { Reaction, i18next } from "/client/api"; import { ServiceConfigHelper } from "../../helpers/util"; import { Template } from "meteor/templating"; +import { Packages } from "/lib/collections"; /** * Accounts helpers @@ -29,7 +30,18 @@ Template.accountsDashboard.helpers({ isShopGuest() { return !_.includes(["dashboard", "admin", "owner"], this.role); }, + /** + * showAvalaraTaxSettings + * @return {Boolean} True if avalara is enabled. Defaults to false if not found + */ + showAvalaraTaxSettings() { + const avalara = Packages.findOne({ + name: "taxes-avalara", + shopId: Reaction.getShopId() + }); + return _.get(avalara, "settings.avalara.enabled", false); + }, /** * members * @return {Boolean} True array of adminsitrative members From f130c12aded3ff614e941d07a33f6f3c064b0449 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Tue, 28 Feb 2017 08:43:33 +0800 Subject: [PATCH 053/120] Fix import order. --- client/modules/accounts/templates/dashboard/dashboard.js | 2 +- client/modules/accounts/templates/tax-settings/tax-settings.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/modules/accounts/templates/dashboard/dashboard.js b/client/modules/accounts/templates/dashboard/dashboard.js index b7b69eb96da..8b8d1aa2228 100644 --- a/client/modules/accounts/templates/dashboard/dashboard.js +++ b/client/modules/accounts/templates/dashboard/dashboard.js @@ -1,8 +1,8 @@ import _ from "lodash"; import { Meteor } from "meteor/meteor"; +import { Template } from "meteor/templating"; import { Reaction, i18next } from "/client/api"; import { ServiceConfigHelper } from "../../helpers/util"; -import { Template } from "meteor/templating"; import { Packages } from "/lib/collections"; /** diff --git a/client/modules/accounts/templates/tax-settings/tax-settings.js b/client/modules/accounts/templates/tax-settings/tax-settings.js index faab82298df..4e7a035359a 100644 --- a/client/modules/accounts/templates/tax-settings/tax-settings.js +++ b/client/modules/accounts/templates/tax-settings/tax-settings.js @@ -1,6 +1,6 @@ import _ from "lodash"; -import { Accounts } from "/lib/collections"; import { Template } from "meteor/templating"; +import { Accounts } from "/lib/collections"; import { Reaction } from "/client/api"; import { TaxEntityCodes } from "/client/collections"; From f9cabb3cdbc2f2c3fdfa7fd26e5e2ae21cbec709 Mon Sep 17 00:00:00 2001 From: Seun Martins Date: Tue, 28 Feb 2017 09:05:58 +0100 Subject: [PATCH 054/120] Setup error handling on Avalara methods --- .../taxes-avalara/server/methods/taxCalc.js | 42 +++++++++++++++---- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js index 495b2f1c36c..33b0093a865 100644 --- a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js +++ b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js @@ -86,9 +86,17 @@ function avaGet(requestUrl, options = {}) { if (pkgData.settings.avalara.enableLogging) { logObject.request = allOptions; } - const result = HTTP.get(requestUrl, allOptions); + + try { + result = HTTP.get(requestUrl, allOptions); + } catch (error) { + result = error; + Logger.error(`Encountered error while calling Avalara API endpoint ${requestUrl}`); + Logger.error(error); + } + if (pkgData.settings.avalara.enableLogging) { - logObject.duration = result.headers.serverDuration; + logObject.duration = _.get(result, "headers.serverDuration"); logObject.result = result.data; Avalogger.info(logObject); } @@ -122,9 +130,19 @@ function avaPost(requestUrl, options) { if (pkgData.settings.avalara.enableLogging) { logObject.request = allOptions; } - const result = HTTP.post(requestUrl, allOptions); + + let result; + + try { + result = HTTP.post(requestUrl, allOptions); + } catch (error) { + Logger.error(`Encountered error while calling API at ${requestUrl}`); + Logger.error(error); + result = {}; + } + if (pkgData.settings.avalara.enableLogging) { - logObject.duration = result.headers.serverDuration; + logObject.duration = _.get(result, "headers.serverDuration"); logObject.result = result.data; Avalogger.info(logObject); } @@ -134,13 +152,13 @@ function avaPost(requestUrl, options) { /** * @summary Gets the full list of Avalara-supported entity use codes. - * @returns {Object} API response + * @returns {Object[]} API response */ taxCalc.getEntityCodes = function () { const baseUrl = getUrl(); const requestUrl = `${baseUrl}definitions/entityusecodes`; const result = avaGet(requestUrl); - return _.get(result, "data.value"); + return _.get(result, "data.value", []); }; // API Methods @@ -178,7 +196,7 @@ taxCalc.validateAddress = function (address) { } let messages; - let validatedAddress; + let validatedAddress = ""; // set default as falsy value const errors = []; const addressToValidate = { line1: address.address1, @@ -197,7 +215,13 @@ taxCalc.validateAddress = function (address) { const baseUrl = getUrl(); const requestUrl = `${baseUrl}/addresses/resolve`; const result = avaPost(requestUrl, { data: addressToValidate }); - const content = JSON.parse(result.content); + let content; + + try { + content = JSON.parse(result.content); + } catch (error) { + content = result.content; + } if (content.messages) { messages = content.messages; } @@ -246,7 +270,7 @@ taxCalc.getTaxCodes = function () { const baseUrl = getUrl(); const requestUrl = `${baseUrl}definitions/taxcodes`; const result = avaGet(requestUrl); - return result.data.value; + return _.get(result, "data.value", []); }; /** From 456760803315a5edbdd4244ac851289c069c1f1e Mon Sep 17 00:00:00 2001 From: Njeri Kieha Date: Fri, 3 Mar 2017 03:15:26 +0300 Subject: [PATCH 055/120] Add dimensions/tax code to PDP (#1931) * Edit product's schema file to include product dimensions * Add product dimensions in variant form * Edit product's schema and product admin form to include country of origin * Edit product's schema and product admin form to include tax description * Edit product's schema and product variant form to include origin country (yet to pre-populate from product's origin country) * Edit variant form to include list of tax codes * Refactor listTaxCodes method * Refactor listTaxCodes method to use template state instead of sessions * Refactor listTaxCodes method to use template state instead of sessions - template state now works * Edit taxCodes schema to include more descriptive fields * Include server method to save tax codes to TaxCodes collection * (First attempt) saving taxcodes to database * Successfully fetching taxcodes from database * Add error block in fetching from db method * change value of taxcode being saved in product details * Adding select2 package * Using select2 to display tax codes in nicer looking select box * Correcting import order * Editing required fields in product schema * Using plain select field to display tax codes * Remove tax code label; add select2 call in onRendered function instead of onCreated * Displaying default tax code as selected in select box * Removing unused meteor package; correcting import order * Throwing more descriptive meteor error on insertTaxCodes method * Display simple input box if tax provider is not enabled --- .meteor/packages | 1 + .meteor/versions | 1 + .../taxes/lib/collections/schemas/taxcodes.js | 13 ++- .../client/styles/products/variantForm.less | 5 +- .../client/components/productAdmin.js | 23 ++++ .../variants/variantForm/variantForm.html | 52 ++++++++- .../variants/variantForm/variantForm.js | 108 +++++++++++++++++- .../included/product-variant/server/index.js | 1 + .../product-variant/server/methods/index.js | 1 + .../server/methods/populateTaxCodes.js | 64 +++++++++++ lib/collections/schemas/products.js | 32 ++++++ 11 files changed, 295 insertions(+), 6 deletions(-) create mode 100644 imports/plugins/included/product-variant/server/methods/index.js create mode 100644 imports/plugins/included/product-variant/server/methods/populateTaxCodes.js diff --git a/.meteor/packages b/.meteor/packages index 630632dac08..9614e420509 100644 --- a/.meteor/packages +++ b/.meteor/packages @@ -96,3 +96,4 @@ johanbrook:publication-collector # Custom Packages +natestrauser:select2 diff --git a/.meteor/versions b/.meteor/versions index 9ff8424fe9a..4fc5031f575 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -122,6 +122,7 @@ mongo@1.1.15 mongo-id@1.0.6 mongo-livedata@1.0.12 mrt:later@1.6.1 +natestrauser:select2@4.0.3 npm-bcrypt@0.9.2 npm-mongo@2.2.16_1 oauth@1.1.13 diff --git a/imports/plugins/core/taxes/lib/collections/schemas/taxcodes.js b/imports/plugins/core/taxes/lib/collections/schemas/taxcodes.js index fdacd208696..67af433b009 100644 --- a/imports/plugins/core/taxes/lib/collections/schemas/taxcodes.js +++ b/imports/plugins/core/taxes/lib/collections/schemas/taxcodes.js @@ -11,12 +11,21 @@ export const TaxCodes = new SimpleSchema({ unique: true }, shopId: { + type: String + }, + taxCode: { type: String, - optional: true + label: "Tax Code" + }, + taxCodeProvider: { + type: String, + label: "Tax Code Provider" }, ssuta: { type: Boolean, - label: "Streamlined Sales Tax" + label: "Streamlined Sales Tax", + optional: true, + defaultValue: false }, title: { type: String, diff --git a/imports/plugins/included/default-theme/client/styles/products/variantForm.less b/imports/plugins/included/default-theme/client/styles/products/variantForm.less index 5642958c68b..ef84730ec30 100644 --- a/imports/plugins/included/default-theme/client/styles/products/variantForm.less +++ b/imports/plugins/included/default-theme/client/styles/products/variantForm.less @@ -26,6 +26,9 @@ margin-bottom: 0px; } .lowInventoryWarningThreshold { - + } } +.select2-container { + width: 370px; +} diff --git a/imports/plugins/included/product-admin/client/components/productAdmin.js b/imports/plugins/included/product-admin/client/components/productAdmin.js index 667fe9e1778..67ec55d1228 100644 --- a/imports/plugins/included/product-admin/client/components/productAdmin.js +++ b/imports/plugins/included/product-admin/client/components/productAdmin.js @@ -21,6 +21,7 @@ const fieldNames = [ "subtitle", "vendor", "description", + "origincountry", "facebookMsg", "twitterMsg", "pinterestMsg", @@ -287,6 +288,28 @@ class ProductAdmin extends Component { ref="descriptionInput" value={this.product.description} /> + + +
+ + {{>afFieldInput name='originCountry' placeholder="Origin Country"}} + {{#if afFieldIsInvalid name='originCountry'}} + {{afFieldMessage name='originCountry'}} + {{/if}} +
+
{{>afFieldInput name='weight' placeholder="0"}} @@ -104,16 +112,56 @@ {{/if}}
+
+ + {{>afFieldInput name='length' placeholder="0"}} + {{#if afFieldIsInvalid name='length'}} + {{afFieldMessage name='length'}} + {{/if}} +
+ +
+ + {{>afFieldInput name='width' placeholder="0"}} + {{#if afFieldIsInvalid name='width'}} + {{afFieldMessage name='width'}} + {{/if}} +
+ +
+ + {{>afFieldInput name='height' placeholder="0"}} + {{#if afFieldIsInvalid name='height'}} + {{afFieldMessage name='height'}} + {{/if}} +
-
+
{{>afFieldInput name='taxable'}} {{#if afFieldIsInvalid name='taxable'}} {{afFieldMessage name='taxable'}} {{/if}}
+ {{#unless checkIfProviderIsEnabled}} +
+ {{>afFieldInput name='taxCode'}} +
+ {{else}} +
+ + {{#if afFieldIsInvalid name='taxCode'}} + {{afFieldMessage name='taxCode'}} + {{/if}} +
+ {{/unless}}
{{>afFieldInput name='inventoryManagement'}} {{#if afFieldIsInvalid name='inventoryManagement'}} @@ -126,7 +174,7 @@ {{afFieldMessage name='inventoryPolicy'}} {{/if}}
-
+
{{>afFieldInput name='lowInventoryWarningThreshold' placeholder="0"}} {{#if afFieldIsInvalid name='lowInventoryWarningThreshold'}} diff --git a/imports/plugins/included/product-variant/client/templates/products/productDetail/variants/variantForm/variantForm.js b/imports/plugins/included/product-variant/client/templates/products/productDetail/variants/variantForm/variantForm.js index 79d8df7e29e..306713615c5 100644 --- a/imports/plugins/included/product-variant/client/templates/products/productDetail/variants/variantForm/variantForm.js +++ b/imports/plugins/included/product-variant/client/templates/products/productDetail/variants/variantForm/variantForm.js @@ -1,13 +1,21 @@ import { Meteor } from "meteor/meteor"; +import { ReactiveDict } from "meteor/reactive-dict"; import { Session } from "meteor/session"; import { Template } from "meteor/templating"; import { Reaction, i18next } from "/client/api"; import { ReactionProduct } from "/lib/api"; import { applyProductRevision } from "/lib/api/products"; -import { Products } from "/lib/collections"; +import { Packages, Products } from "/lib/collections"; +import { TaxCodes } from "/imports/plugins/core/taxes/lib/collections"; Template.variantForm.onCreated(function () { + this.state = new ReactiveDict(); + this.state.set("taxCodes", []); + this.autorun(() => { + // subscribe to TaxCodes + Meteor.subscribe("TaxCodes"); + const productHandle = Reaction.Router.getParam("handle"); if (!productHandle) { @@ -21,6 +29,12 @@ Template.variantForm.onCreated(function () { }; }); +Template.variantForm.onRendered(function () { + $("#taxCode").select2({ + placeholder: "Select Tax Rate" + }); +}); + /** * variantForm helpers */ @@ -68,6 +82,11 @@ Template.variantForm.helpers({ return "display:none;"; } }, + displayTaxCodes: function () { + if (this.taxable !== true) { + return "display:none;"; + } + }, removeVariant(variant) { return () => { return () => { @@ -114,6 +133,74 @@ Template.variantForm.helpers({ }); }; }; + }, + checkIfProviderIsEnabled: function () { + const shopId = Reaction.getShopId(); + + const provider = Packages.findOne({ + "shopId": shopId, + "registry.provides": "taxCodes", + "$where": function () { + const providerName = this.name.split("-")[1]; + return this.settings[providerName].enabled; + } + }); + + if (provider) { + return true; + } + }, + listTaxCodes: function () { + const instance = Template.instance(); + const shopId = Reaction.getShopId(); + + const provider = Packages.findOne({ + "shopId": shopId, + "registry.provides": "taxCodes", + "$where": function () { + const providerName = this.name.split("-")[1]; + return this.settings[providerName].enabled; + } + }); + + if (provider) { + if (Meteor.subscribe("TaxCodes").ready() && TaxCodes.find({}).count() === 0) { + Meteor.call(provider.settings.taxCodes.getTaxCodeMethod, (error, result) => { + result.forEach(function (code) { + Meteor.call("taxes/insertTaxCodes", shopId, code, provider.name, (err) => { + if (err) { + throw new Meteor.Error("Error populating TaxCodes collection", err); + } + return; + }); + }); + }); + Meteor.call("taxes/fetchTaxCodes", shopId, provider.name, (err, res) => { + if (err) { + throw new Meteor.Error("Error fetching records", err); + } else { + instance.state.set("taxCodes", res); + } + }); + } else { + Meteor.call("taxes/fetchTaxCodes", shopId, provider.name, (err, res) => { + if (err) { + throw new Meteor.Error("Error fetching records", err); + } else { + instance.state.set("taxCodes", res); + } + }); + } + } else { + return false; + } + return instance.state.get("taxCodes"); + }, + displayCode: function () { + if (this.taxCode && this.taxCode !== "00000") { + return this.taxCode; + } + return "Select Tax Code"; } }); @@ -140,6 +227,25 @@ Template.variantForm.events({ }); } } + } else if (field === "taxCode") { + const value = Template.instance().$(event.currentTarget).prop("value"); + Meteor.call("products/updateProductField", template.data._id, field, value, + error => { + if (error) { + throw new Meteor.Error("error updating variant", error); + } + }); + if (ReactionProduct.checkChildVariants(template.data._id) > 0) { + const childVariants = ReactionProduct.getVariants(template.data._id); + for (const child of childVariants) { + Meteor.call("products/updateProductField", child._id, field, value, + error => { + if (error) { + throw new Meteor.Error("error updating variant", error); + } + }); + } + } } // template.$(formId).submit(); // ReactionProduct.setCurrentVariant(template.data._id); diff --git a/imports/plugins/included/product-variant/server/index.js b/imports/plugins/included/product-variant/server/index.js index 3979f964b5a..2f312565ff9 100644 --- a/imports/plugins/included/product-variant/server/index.js +++ b/imports/plugins/included/product-variant/server/index.js @@ -1 +1,2 @@ import "./i18n"; +import "./methods"; diff --git a/imports/plugins/included/product-variant/server/methods/index.js b/imports/plugins/included/product-variant/server/methods/index.js new file mode 100644 index 00000000000..919619be0b4 --- /dev/null +++ b/imports/plugins/included/product-variant/server/methods/index.js @@ -0,0 +1 @@ +import "./populateTaxCodes.js"; diff --git a/imports/plugins/included/product-variant/server/methods/populateTaxCodes.js b/imports/plugins/included/product-variant/server/methods/populateTaxCodes.js new file mode 100644 index 00000000000..2ffe36ecd07 --- /dev/null +++ b/imports/plugins/included/product-variant/server/methods/populateTaxCodes.js @@ -0,0 +1,64 @@ +import { Meteor } from "meteor/meteor"; +import { TaxCodes } from "/imports/plugins/core/taxes/lib/collections"; + +const taxCodes = {}; + +/* + * taxes/insertTaxCodes + * @summary populate TaxCodes collection + * @param {String} shopID - current shop's id + * @param {Object} code - tax code object to insert into TaxCodes collection + * @param {String} providerName - tax code provider + * @return {} undefined + */ +taxCodes.populateTaxCodes = function (shopId, code, providerName) { + check(shopId, String); + check(code, Object); + check(providerName, String); + + try { + TaxCodes.insert({ + id: code.id, + shopId: shopId, + taxCode: code.taxCode, + taxCodeProvider: providerName, + ssuta: code.isSSTCertified, + label: code.description, + parent: code.parentTaxCode + }); + } catch (err) { + throw new Meteor.Error("Error populating TaxCodes collection", err); + } +}; + +/* + * taxes/getTaxCodes + * @summary fetch tax codes from TaxCodes collection + * @param {String} shopID - current shop's id + * @param {String} provider - tax code provider + * @return {Array} array of tax codes + */ +taxCodes.fetchTaxCodes = function (shopId, provider) { + check(shopId, String); + check(provider, String); + + const taxCodesArray = []; + + const codes = TaxCodes.find({ + shopId: shopId, + taxCodeProvider: provider + }); + + codes.forEach(function (code) { + taxCodesArray.push({ + value: code.taxCode, + label: `${code.taxCode} | ${code.label}` + }); + }); + return taxCodesArray; +}; + +Meteor.methods({ + "taxes/insertTaxCodes": taxCodes.populateTaxCodes, + "taxes/fetchTaxCodes": taxCodes.fetchTaxCodes +}); diff --git a/lib/collections/schemas/products.js b/lib/collections/schemas/products.js index 18eed9f474f..962aa3d5504 100644 --- a/lib/collections/schemas/products.js +++ b/lib/collections/schemas/products.js @@ -138,6 +138,24 @@ export const ProductVariant = new SimpleSchema({ } } }, + length: { + label: "Length", + type: Number, + min: 0, + optional: true + }, + width: { + label: "Width", + type: Number, + min: 0, + optional: true + }, + height: { + label: "Height", + type: Number, + min: 0, + optional: true + }, inventoryManagement: { type: Boolean, label: "Inventory Tracking", @@ -276,6 +294,11 @@ export const ProductVariant = new SimpleSchema({ workflow: { type: Workflow, optional: true + }, + + originCountry: { + type: String, + optional: true } }); @@ -329,6 +352,15 @@ export const Product = new SimpleSchema({ type: String, optional: true }, + originCountry: { + type: String, + optional: true + }, + taxDescription: { + type: String, + optional: true, + label: "Tax Description" + }, type: { label: "Type", type: String, From d7e458a26a8066932932f44eb7957204fa48a3fc Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Fri, 3 Mar 2017 10:32:30 +0800 Subject: [PATCH 056/120] Correct the way we test for validation on address2 --- server/methods/accounts/accounts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/methods/accounts/accounts.js b/server/methods/accounts/accounts.js index b2c220805d8..676618acd5e 100644 --- a/server/methods/accounts/accounts.js +++ b/server/methods/accounts/accounts.js @@ -65,7 +65,7 @@ function compareAddress(address, validationAddress) { errors.push({ address1: "Address line one did not validate" }); } - if (address.address2 && !validationAddress.address2) { + if (address.address2 && validationAddress.address2 && _.trim(_.upperCase(address.address2)) !== _.trim(_.upperCase(validationAddress.address2))) { errors.push({ address2: "Address line 2 did not validate" }); } From 646f3e21c4d6bc366f7c59428720840ebf79bea6 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Fri, 3 Mar 2017 10:32:53 +0800 Subject: [PATCH 057/120] Remove extra panel divs --- .../client/settings/avalara.html | 49 +++++-------------- 1 file changed, 13 insertions(+), 36 deletions(-) diff --git a/imports/plugins/included/taxes-avalara/client/settings/avalara.html b/imports/plugins/included/taxes-avalara/client/settings/avalara.html index 430e05ff6b0..2a27bbbb669 100644 --- a/imports/plugins/included/taxes-avalara/client/settings/avalara.html +++ b/imports/plugins/included/taxes-avalara/client/settings/avalara.html @@ -10,43 +10,20 @@ {{#autoForm collection=Collections.Packages schema=packageConfigSchema doc=packageData type="update" id="avalara-update-form"}}
{{>afQuickField name='settings.avalara.apiLoginId' class='form-control'}} -
-
- {{>afQuickField name='settings.avalara.username' class='form-control'}} -
-
- {{>afQuickField name='settings.avalara.password' class='form-control' type='password'}} -
-
- {{>afQuickField name='settings.avalara.companyCode' class='form-control'}} -
-
- {{>afQuickField name='settings.avalara.shippingTaxCode' class='form-control'}} -
-
- {{>afQuickField name='settings.avalara.mode' class='form-control'}} -
-
- {{>afQuickField name='settings.avalara.performTaxCalculation' class='form-control'}} -
-
- {{>afQuickField name='settings.addressValidation.enabled' class='form-control'}} -
-
- {{> afQuickField name="settings.addressValidation.countryList" type="select-checkbox" options=countryOptions value=country }} -
-
- {{>afQuickField name='settings.avalara.enableLogging' class='form-control'}} -
-
- {{>afQuickField name='settings.avalara.logRetentionDuration' class='form-control'}} -
-
- {{>afQuickField name='settings.avalara.requestTimeout' class='form-control'}} -
-
- {{>afQuickField name='settings.avalara.commitDocuments' class='form-control'}} + {{>afQuickField name='settings.avalara.username' class='form-control'}} + {{>afQuickField name='settings.avalara.password' class='form-control' type='password'}} + {{>afQuickField name='settings.avalara.companyCode' class='form-control'}} + {{>afQuickField name='settings.avalara.shippingTaxCode' class='form-control'}} + {{>afQuickField name='settings.avalara.mode' class='form-control'}} + {{>afQuickField name='settings.avalara.performTaxCalculation' class='form-control'}} + {{>afQuickField name='settings.addressValidation.enabled' class='form-control'}} + {{>afQuickField name="settings.addressValidation.countryList" type="select-checkbox" options=countryOptions value=country }} + {{>afQuickField name='settings.avalara.enableLogging' class='form-control'}} + {{>afQuickField name='settings.avalara.logRetentionDuration' class='form-control'}} + {{>afQuickField name='settings.avalara.requestTimeout' class='form-control'}} + {{>afQuickField name='settings.avalara.commitDocuments' class='form-control'}}
+ From cae3d1e4fd95d4a791617d71941cfc7e5b8213f1 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Fri, 3 Mar 2017 10:33:21 +0800 Subject: [PATCH 058/120] Add account ID to account management screen --- client/modules/accounts/templates/members/member.html | 4 ++++ client/modules/accounts/templates/members/member.js | 3 +++ 2 files changed, 7 insertions(+) diff --git a/client/modules/accounts/templates/members/member.html b/client/modules/accounts/templates/members/member.html index 007d9e31035..a67be7674af 100644 --- a/client/modules/accounts/templates/members/member.html +++ b/client/modules/accounts/templates/members/member.html @@ -7,6 +7,10 @@ {{displayName this}}  ({{this.email}})
+
+ Customer Id: {{userId}} +
+
diff --git a/client/modules/accounts/templates/members/member.js b/client/modules/accounts/templates/members/member.js index d590413bde3..d756d8b6edb 100644 --- a/client/modules/accounts/templates/members/member.js +++ b/client/modules/accounts/templates/members/member.js @@ -36,6 +36,9 @@ Template.memberSettings.helpers({ } } }, + userId: function () { + return Meteor.userId(); + }, hasPermissionChecked: function (permission, userId) { if (userId && Roles.userIsInRole(userId, permission, this.shopId || Roles.userIsInRole(userId, permission, Roles.GLOBAL_GROUP))) { From b7f9c7d0f0fc274978f5dc19d2864262027b1155 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Fri, 3 Mar 2017 11:35:29 +0800 Subject: [PATCH 059/120] Adjust log details for when there is no document type --- .../client/settings/avagriddle.js | 27 ++++++++++--------- .../client/settings/avalara.html | 3 ++- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/imports/plugins/included/taxes-avalara/client/settings/avagriddle.js b/imports/plugins/included/taxes-avalara/client/settings/avagriddle.js index 81a7a7effbc..f385c8e0ea0 100644 --- a/imports/plugins/included/taxes-avalara/client/settings/avagriddle.js +++ b/imports/plugins/included/taxes-avalara/client/settings/avagriddle.js @@ -1,4 +1,5 @@ /* eslint react/prop-types:0, react/jsx-sort-props:0, react/forbid-prop-types: 0, "react/prefer-es6-class": [1, "never"] */ +import _ from "lodash"; import React from "react"; import moment from "moment"; import Griddle from "griddle-react"; @@ -21,29 +22,31 @@ const LogGriddle = React.createClass({ externalResultsPerPage: this.props.externalResultsPerPage, externalSortColumn: this.props.externalSortColumn, externalSortAscending: this.props.externalSortAscending - } + }; }, getMeteorData() { const matchingResults = Counts.get(this.props.matchingResultsCount); const pubHandle = Meteor.subscribe(this.props.publication, this.props.subscriptionParams); const rawResults = this.props.collection.find({}).fetch(); - const results = rawResults.map((o) => { - return { - date: moment(o.data).format("MM/DD/YYYY HH:mm:ss"), - docType: o.data.request.data.type, - request: JSON.stringify(o.data.request), - result: JSON.stringify(o.data.result), - _id: o._id - } - }); - + let results; + if (rawResults) { + results = rawResults.map((o) => { + return { + date: moment(o.data).format("MM/DD/YYYY HH:mm:ss"), + docType: _.get(o, "data.request.data.type", ""), + request: JSON.stringify(o.data.request), + result: JSON.stringify(o.data.result), + _id: o._id + }; + }); + } return { loading: !pubHandle.ready(), results, matchingResults - } + }; }, setPage(index) { diff --git a/imports/plugins/included/taxes-avalara/client/settings/avalara.html b/imports/plugins/included/taxes-avalara/client/settings/avalara.html index 2a27bbbb669..407205cdf96 100644 --- a/imports/plugins/included/taxes-avalara/client/settings/avalara.html +++ b/imports/plugins/included/taxes-avalara/client/settings/avalara.html @@ -45,7 +45,8 @@
{{#if instance.state.get 'isEditing'}} {{#if instance.state.get 'editingId'}} -
+

Log Detail

+
{{#autoForm collection=logCollection schema=logSchema From b68265dfe1f06b79b98eced0e1490c6a6a4ad4ae Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Fri, 3 Mar 2017 11:36:18 +0800 Subject: [PATCH 060/120] Rename files to match style guide --- .../tax-settings/{tax-settings.html => taxSettings.html} | 0 .../templates/tax-settings/{tax-settings.js => taxSettings.js} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename client/modules/accounts/templates/tax-settings/{tax-settings.html => taxSettings.html} (100%) rename client/modules/accounts/templates/tax-settings/{tax-settings.js => taxSettings.js} (100%) diff --git a/client/modules/accounts/templates/tax-settings/tax-settings.html b/client/modules/accounts/templates/tax-settings/taxSettings.html similarity index 100% rename from client/modules/accounts/templates/tax-settings/tax-settings.html rename to client/modules/accounts/templates/tax-settings/taxSettings.html diff --git a/client/modules/accounts/templates/tax-settings/tax-settings.js b/client/modules/accounts/templates/tax-settings/taxSettings.js similarity index 100% rename from client/modules/accounts/templates/tax-settings/tax-settings.js rename to client/modules/accounts/templates/tax-settings/taxSettings.js From 5d3fdedccb298e9d245dc18f8ced2a146cfac787 Mon Sep 17 00:00:00 2001 From: Seun Martins Date: Fri, 3 Mar 2017 11:04:47 +0100 Subject: [PATCH 061/120] Use individual taxSettings form each account --- .../templates/dashboard/dashboard.html | 30 ++++++--------- .../accounts/templates/dashboard/dashboard.js | 10 +++-- .../templates/tax-settings/taxSettings.html | 38 ++++++++++--------- .../templates/tax-settings/taxSettings.js | 16 ++++---- .../taxes-avalara/server/methods/taxCalc.js | 2 +- 5 files changed, 47 insertions(+), 49 deletions(-) diff --git a/client/modules/accounts/templates/dashboard/dashboard.html b/client/modules/accounts/templates/dashboard/dashboard.html index 6da5107fe89..222396dfc9e 100644 --- a/client/modules/accounts/templates/dashboard/dashboard.html +++ b/client/modules/accounts/templates/dashboard/dashboard.html @@ -12,11 +12,14 @@

    - {{#each members}} - {{#if isShopMember}} + {{#each accountMember in members}} + {{#if isShopMember accountMember }}
  • - {{> member}} + {{> member accountMember }}
  • + {{#if showAvalaraTaxSettings }} + {{> taxSettingsPanel member=accountMember}} + {{/if}} {{/if}} {{/each}}
@@ -30,27 +33,18 @@

    - {{#each members}} - {{#if isShopGuest}} + {{#each guestMember in members}} + {{#if isShopGuest guestMember}}
  • - {{> member}} + {{> member guestMember}}
  • + {{#if showAvalaraTaxSettings }} + {{> taxSettingsPanel member=guestMember }} + {{/if}} {{/if}} {{/each}}
- - {{#if showAvalaraTaxSettings }} -
-
-

- Tax Exempt Settings -

-
- {{> taxSettingsPanel}} -
- {{/if}} -
{{/if}} diff --git a/client/modules/accounts/templates/dashboard/dashboard.js b/client/modules/accounts/templates/dashboard/dashboard.js index 8b8d1aa2228..eba44110383 100644 --- a/client/modules/accounts/templates/dashboard/dashboard.js +++ b/client/modules/accounts/templates/dashboard/dashboard.js @@ -17,18 +17,20 @@ Template.accountsDashboard.onCreated(function () { Template.accountsDashboard.helpers({ /** * isShopMember + * @param {Object} member member object * @return {Boolean} True if the memnber is an administrator */ - isShopMember() { - return _.includes(["dashboard", "admin", "owner"], this.role); + isShopMember(member) { + return _.includes(["dashboard", "admin", "owner"], member.role); }, /** * isShopGuest + * @param {Object} member member object * @return {Boolean} True if the member is a guest */ - isShopGuest() { - return !_.includes(["dashboard", "admin", "owner"], this.role); + isShopGuest(member) { + return !_.includes(["dashboard", "admin", "owner"], member.role); }, /** * showAvalaraTaxSettings diff --git a/client/modules/accounts/templates/tax-settings/taxSettings.html b/client/modules/accounts/templates/tax-settings/taxSettings.html index e1fb68b9120..1e6f0ddb4dd 100644 --- a/client/modules/accounts/templates/tax-settings/taxSettings.html +++ b/client/modules/accounts/templates/tax-settings/taxSettings.html @@ -1,22 +1,24 @@ diff --git a/client/modules/accounts/templates/tax-settings/taxSettings.js b/client/modules/accounts/templates/tax-settings/taxSettings.js index 4e7a035359a..56fb5bf0e00 100644 --- a/client/modules/accounts/templates/tax-settings/taxSettings.js +++ b/client/modules/accounts/templates/tax-settings/taxSettings.js @@ -14,17 +14,17 @@ Template.taxSettingsPanel.helpers({ return null; }, entityCodes() { - return _.concat(TaxEntityCodes.find().map((entityCode) => { + const codes = _.concat(TaxEntityCodes.find().map((entityCode) => { return _.assign({}, entityCode, { label: entityCode.name, value: entityCode.code }); - }), [ - { - label: "SET CUSTOM VALUE", - value: "CUSTOM USER INPUT" - } - ]); + }), [{ + label: "SET CUSTOM VALUE", + value: "CUSTOM USER INPUT" + }]); + + return { codes, showForm: codes.length > 1 }; } }); @@ -43,7 +43,7 @@ Template.taxSettingsPanel.onCreated(function () { this.autorun(() => { this.subscribe("Account"); }); - + // Todo: Fix: currently called for each customer. To be called once Meteor.call("avalara/getEntityCodes", (error, entityCodes) => { _.each(entityCodes, (entityCode) => { TaxEntityCodes.insert(entityCode); diff --git a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js index 33b0093a865..d927fb8c330 100644 --- a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js +++ b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js @@ -157,7 +157,7 @@ function avaPost(requestUrl, options) { taxCalc.getEntityCodes = function () { const baseUrl = getUrl(); const requestUrl = `${baseUrl}definitions/entityusecodes`; - const result = avaGet(requestUrl); + const result = avaGet(requestUrl, { timeout: 5000 }); return _.get(result, "data.value", []); }; From 3905c451227f8e2df1db17353de0b329efdf05f1 Mon Sep 17 00:00:00 2001 From: Seun Martins Date: Fri, 3 Mar 2017 11:49:55 +0100 Subject: [PATCH 062/120] Add check to prevent populating duplicates --- .../accounts/templates/tax-settings/taxSettings.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/client/modules/accounts/templates/tax-settings/taxSettings.js b/client/modules/accounts/templates/tax-settings/taxSettings.js index 56fb5bf0e00..4184362be4e 100644 --- a/client/modules/accounts/templates/tax-settings/taxSettings.js +++ b/client/modules/accounts/templates/tax-settings/taxSettings.js @@ -45,9 +45,12 @@ Template.taxSettingsPanel.onCreated(function () { }); // Todo: Fix: currently called for each customer. To be called once Meteor.call("avalara/getEntityCodes", (error, entityCodes) => { - _.each(entityCodes, (entityCode) => { - TaxEntityCodes.insert(entityCode); - }); + const currentCodes = TaxEntityCodes.find().fetch(); + if (!currentCodes.length) { + _.each(entityCodes, (entityCode) => { + TaxEntityCodes.insert(entityCode); + }); + } }); }); From 8278baf7404fb7067573cb169a4f731ac75515ea Mon Sep 17 00:00:00 2001 From: Seun Martins Date: Fri, 3 Mar 2017 12:29:51 +0100 Subject: [PATCH 063/120] Fix reference to accountschema --- .../modules/accounts/templates/tax-settings/taxSettings.html | 1 - client/modules/accounts/templates/tax-settings/taxSettings.js | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/client/modules/accounts/templates/tax-settings/taxSettings.html b/client/modules/accounts/templates/tax-settings/taxSettings.html index 1e6f0ddb4dd..6fe1d37f87d 100644 --- a/client/modules/accounts/templates/tax-settings/taxSettings.html +++ b/client/modules/accounts/templates/tax-settings/taxSettings.html @@ -3,7 +3,6 @@

Tax Exempt Settings

- {{ member.userId }} {{#autoForm collection=Collections.Accounts schema=accountsSchema doc=member type="update" id="tax-settings-form"}} {{>afQuickField name='taxSettings.exemptionNo' class='form-control'}} {{#if entityCodes.showForm}} diff --git a/client/modules/accounts/templates/tax-settings/taxSettings.js b/client/modules/accounts/templates/tax-settings/taxSettings.js index 4184362be4e..0eeba69594a 100644 --- a/client/modules/accounts/templates/tax-settings/taxSettings.js +++ b/client/modules/accounts/templates/tax-settings/taxSettings.js @@ -1,6 +1,7 @@ import _ from "lodash"; import { Template } from "meteor/templating"; import { Accounts } from "/lib/collections"; +import { Accounts as AccountsSchema } from "/lib/collections/accounts"; import { Reaction } from "/client/api"; import { TaxEntityCodes } from "/client/collections"; @@ -13,6 +14,9 @@ Template.taxSettingsPanel.helpers({ } return null; }, + accountsSchema() { + return AccountsSchema; + }, entityCodes() { const codes = _.concat(TaxEntityCodes.find().map((entityCode) => { return _.assign({}, entityCode, { From 9b14db9661ef8b9cc8fd0a596d79529f13653b20 Mon Sep 17 00:00:00 2001 From: Seun Martins Date: Fri, 3 Mar 2017 13:41:43 +0100 Subject: [PATCH 064/120] Fix form update to corresponding accounts --- .../templates/tax-settings/taxSettings.html | 22 +++++++++---------- .../templates/tax-settings/taxSettings.js | 11 +++++----- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/client/modules/accounts/templates/tax-settings/taxSettings.html b/client/modules/accounts/templates/tax-settings/taxSettings.html index 6fe1d37f87d..4cfd9288a4b 100644 --- a/client/modules/accounts/templates/tax-settings/taxSettings.html +++ b/client/modules/accounts/templates/tax-settings/taxSettings.html @@ -3,19 +3,17 @@

Tax Exempt Settings

- {{#autoForm collection=Collections.Accounts schema=accountsSchema doc=member type="update" id="tax-settings-form"}} + {{#autoForm collection=Collections.Accounts schema=accountsSchema doc=account id=makeUniqueId type="update" }} {{>afQuickField name='taxSettings.exemptionNo' class='form-control'}} - {{#if entityCodes.showForm}} - {{> afQuickField data-event-action="customType" name="taxSettings.customerUsageType" type="select" options=entityCodes.codes }} -

Current Saved Entity Code: {{ member.taxSettings.customerUsageType }}

-
- - -
-
- -
- {{/if}} + {{> afQuickField data-event-action="customType" name="taxSettings.customerUsageType" type="select" options=entityCodes }} +

Current Saved Entity Code: {{ account.taxSettings.customerUsageType }}

+
+ + +
+
+ +
{{/autoForm}}
diff --git a/client/modules/accounts/templates/tax-settings/taxSettings.js b/client/modules/accounts/templates/tax-settings/taxSettings.js index 0eeba69594a..a9b456f63ff 100644 --- a/client/modules/accounts/templates/tax-settings/taxSettings.js +++ b/client/modules/accounts/templates/tax-settings/taxSettings.js @@ -1,7 +1,7 @@ import _ from "lodash"; import { Template } from "meteor/templating"; import { Accounts } from "/lib/collections"; -import { Accounts as AccountsSchema } from "/lib/collections/accounts"; +import { Accounts as AccountsSchema } from "/lib/collections/schemas/accounts"; import { Reaction } from "/client/api"; import { TaxEntityCodes } from "/client/collections"; @@ -9,16 +9,19 @@ Template.taxSettingsPanel.helpers({ account() { if (Reaction.Subscriptions.Account.ready()) { return Accounts.findOne({ - userId: Meteor.userId() + _id: this.member.userId }); } return null; }, + makeUniqueId() { + return `tax-settings-form-${this.member.userId}`; + }, accountsSchema() { return AccountsSchema; }, entityCodes() { - const codes = _.concat(TaxEntityCodes.find().map((entityCode) => { + return _.concat(TaxEntityCodes.find().map((entityCode) => { return _.assign({}, entityCode, { label: entityCode.name, value: entityCode.code @@ -27,8 +30,6 @@ Template.taxSettingsPanel.helpers({ label: "SET CUSTOM VALUE", value: "CUSTOM USER INPUT" }]); - - return { codes, showForm: codes.length > 1 }; } }); From d8b0527203650011adf8608c9c6f6369c86615a8 Mon Sep 17 00:00:00 2001 From: Seun Martins Date: Fri, 3 Mar 2017 20:09:14 +0100 Subject: [PATCH 065/120] Add unique tax setting by customer to Avalara payload --- .../included/taxes-avalara/server/methods/taxCalc.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js index d927fb8c330..c7d0d2724e6 100644 --- a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js +++ b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js @@ -55,10 +55,11 @@ function getAuthData(packageData = taxCalc.getPackageData()) { /** * @summary Get exempt tax settings to pass to REST API + * @param {String} userId id of user to find settings * @returns {Object} containing exemptCode and customerUsageType */ -function getTaxSettings() { - return _.get(Accounts.findOne(), "taxSettings"); +function getTaxSettings(userId) { + return _.get(Accounts.findOne({ _id: userId }), "taxSettings"); } /** @@ -361,7 +362,7 @@ taxCalc.estimateCart = function (cart, callback) { check(callback, Function); if (cart.items && cart.shipping && cart.shipping[0].address) { - const salesOrder = _.assign({}, cartToSalesOrder(cart), getTaxSettings()); + const salesOrder = _.assign({}, cartToSalesOrder(cart), getTaxSettings(cart.userId)); const baseUrl = getUrl(); const requestUrl = `${baseUrl}/transactions/create`; const result = avaPost(requestUrl, { data: salesOrder }); @@ -458,7 +459,7 @@ taxCalc.recordOrder = function (order, callback) { check(callback, Function); // unlike the other functions, we expect this to always be called asynchronously if (order && order.shipping && order.shipping[0].address) { - const salesOrder = _.assign({}, orderToSalesInvoice(order), getTaxSettings()); + const salesOrder = _.assign({}, orderToSalesInvoice(order), getTaxSettings(order.userId)); const baseUrl = getUrl(); const requestUrl = `${baseUrl}/transactions/create`; try { From dfec5ba9c653b0a0f14cca3e709a5054964d8269 Mon Sep 17 00:00:00 2001 From: Seun Martins Date: Fri, 3 Mar 2017 20:10:49 +0100 Subject: [PATCH 066/120] Fix exception error on geoCoder call --- server/methods/accounts/accounts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/methods/accounts/accounts.js b/server/methods/accounts/accounts.js index 676618acd5e..cce0e9bc93c 100644 --- a/server/methods/accounts/accounts.js +++ b/server/methods/accounts/accounts.js @@ -43,7 +43,7 @@ function getValidator() { } } const packageKey = registryName.split("/")[2]; // "taxes/addressValidation/{packageKey}" - if (!geoCoder.settings[packageKey].enabled) { + if (!_.get(geoCoder.settings[packageKey], "enabled")) { return ""; } From b8090857183ecf1ee9a8ea01159c4e445c47609f Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Mon, 6 Mar 2017 09:24:27 +0800 Subject: [PATCH 067/120] Only pass in regions when it's a "country with regions" --- .../taxes-avalara/server/methods/taxCalc.js | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js index c7d0d2724e6..7b1ac9feb99 100644 --- a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js +++ b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js @@ -331,12 +331,15 @@ function cartToSalesOrder(cart) { line1: cart.shipping[0].address.address1, line2: cart.shipping[0].address.address2 || "", city: cart.shipping[0].address.city, - region: cart.shipping[0].address.region, country: cart.shipping[0].address.country || "US" } }, lines: lineItems }; + // Don't add a region unless this is a "country with regions" + if (_.includes(countriesWithRegions, cart.shipping[0].address.country)) { + salesOrder.ShipTo.region = cart.shipping[0].address.region; + } // current "coupon code" discount are based at the cart level, and every iten has it's // discounted property set to true. @@ -431,13 +434,16 @@ function orderToSalesInvoice(order) { line1: order.shipping[0].address.address1, line2: order.shipping[0].address.address2 || "", city: order.shipping[0].address.city, - region: order.shipping[0].address.region, country: order.shipping[0].address.country || "US" } }, lines: lineItems }; + // Don't add a region unless this is a "country with regions" + if (_.includes(countriesWithRegions, order.shipping[0].address.country)) { + salesInvoice.ShipTo.region = order.shipping[0].address.region; + } if (order.discount) { salesInvoice.discount = accounting.toFixed(order.discount, 2); for (const line of salesInvoice.lines) { @@ -490,6 +496,8 @@ taxCalc.reportRefund = function (order, refundAmount, callback) { const baseUrl = getUrl(); const requestUrl = `${baseUrl}/transactions/create`; const returnAmount = refundAmount * -1; + const orderDate = moment(order.createdAt).format(); // original date of the order for tax calculation purposes + const returnDate = moment().format(); // the date the return is being processed const lineItems = { number: "01", quantity: 1, @@ -502,8 +510,8 @@ taxCalc.reportRefund = function (order, refundAmount, callback) { code: order.cartId, commit: true, customerCode: order._id, - taxDate: moment.utc(order.createdAt), - date: moment(), + taxDate: orderDate, + date: returnDate, currencyCode: currencyCode, addresses: { ShipFrom: { @@ -518,14 +526,16 @@ taxCalc.reportRefund = function (order, refundAmount, callback) { line1: order.shipping[0].address.address1, line2: order.shipping[0].address.address2 || "", city: order.shipping[0].address.city, - region: order.shipping[0].address.region, country: order.shipping[0].address.country || "US" } }, lines: [lineItems] }; - + // Don't add a region unless this is a "country with regions" + if (_.includes(countriesWithRegions, order.shipping[0].address.country)) { + returnInvoice.ShipTo.region = order.shipping[0].address.region; + } const result = avaPost(requestUrl, { data: returnInvoice }); return callback(result.data); }; From 064aa3a9f11ecc09feb398599683689e2548a4ef Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Mon, 6 Mar 2017 10:42:21 +0800 Subject: [PATCH 068/120] Revert "Only pass in regions when it's a "country with regions"" This reverts commit b8090857183ecf1ee9a8ea01159c4e445c47609f. --- .../taxes-avalara/server/methods/taxCalc.js | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js index 7b1ac9feb99..c7d0d2724e6 100644 --- a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js +++ b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js @@ -331,15 +331,12 @@ function cartToSalesOrder(cart) { line1: cart.shipping[0].address.address1, line2: cart.shipping[0].address.address2 || "", city: cart.shipping[0].address.city, + region: cart.shipping[0].address.region, country: cart.shipping[0].address.country || "US" } }, lines: lineItems }; - // Don't add a region unless this is a "country with regions" - if (_.includes(countriesWithRegions, cart.shipping[0].address.country)) { - salesOrder.ShipTo.region = cart.shipping[0].address.region; - } // current "coupon code" discount are based at the cart level, and every iten has it's // discounted property set to true. @@ -434,16 +431,13 @@ function orderToSalesInvoice(order) { line1: order.shipping[0].address.address1, line2: order.shipping[0].address.address2 || "", city: order.shipping[0].address.city, + region: order.shipping[0].address.region, country: order.shipping[0].address.country || "US" } }, lines: lineItems }; - // Don't add a region unless this is a "country with regions" - if (_.includes(countriesWithRegions, order.shipping[0].address.country)) { - salesInvoice.ShipTo.region = order.shipping[0].address.region; - } if (order.discount) { salesInvoice.discount = accounting.toFixed(order.discount, 2); for (const line of salesInvoice.lines) { @@ -496,8 +490,6 @@ taxCalc.reportRefund = function (order, refundAmount, callback) { const baseUrl = getUrl(); const requestUrl = `${baseUrl}/transactions/create`; const returnAmount = refundAmount * -1; - const orderDate = moment(order.createdAt).format(); // original date of the order for tax calculation purposes - const returnDate = moment().format(); // the date the return is being processed const lineItems = { number: "01", quantity: 1, @@ -510,8 +502,8 @@ taxCalc.reportRefund = function (order, refundAmount, callback) { code: order.cartId, commit: true, customerCode: order._id, - taxDate: orderDate, - date: returnDate, + taxDate: moment.utc(order.createdAt), + date: moment(), currencyCode: currencyCode, addresses: { ShipFrom: { @@ -526,16 +518,14 @@ taxCalc.reportRefund = function (order, refundAmount, callback) { line1: order.shipping[0].address.address1, line2: order.shipping[0].address.address2 || "", city: order.shipping[0].address.city, + region: order.shipping[0].address.region, country: order.shipping[0].address.country || "US" } }, lines: [lineItems] }; - // Don't add a region unless this is a "country with regions" - if (_.includes(countriesWithRegions, order.shipping[0].address.country)) { - returnInvoice.ShipTo.region = order.shipping[0].address.region; - } + const result = avaPost(requestUrl, { data: returnInvoice }); return callback(result.data); }; From 1663ccc3c536de80df335a1dc6fda598310b4ded Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Mon, 6 Mar 2017 11:04:59 +0800 Subject: [PATCH 069/120] Create a refundReference which is cartId + date --- .../included/taxes-avalara/server/methods/taxCalc.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js index c7d0d2724e6..0cc4b57a9f3 100644 --- a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js +++ b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js @@ -490,6 +490,9 @@ taxCalc.reportRefund = function (order, refundAmount, callback) { const baseUrl = getUrl(); const requestUrl = `${baseUrl}/transactions/create`; const returnAmount = refundAmount * -1; + const orderDate = moment(order.createdAt).format(); + const refundDate = moment().format(); + const refundReference = `${order.cartId}:${refundDate}`; const lineItems = { number: "01", quantity: 1, @@ -499,11 +502,11 @@ taxCalc.reportRefund = function (order, refundAmount, callback) { const returnInvoice = { companyCode: companyCode, type: "ReturnInvoice", - code: order.cartId, + code: refundReference, commit: true, customerCode: order._id, - taxDate: moment.utc(order.createdAt), - date: moment(), + taxDate: orderDate, + date: refundDate, currencyCode: currencyCode, addresses: { ShipFrom: { From 114f9132ceeaa1df638cfd9dd27276f5a235d3e0 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Mon, 6 Mar 2017 14:25:28 +0800 Subject: [PATCH 070/120] Ignore line items are not marked taxable --- .../taxes-avalara/server/methods/taxCalc.js | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js index 0cc4b57a9f3..978d7d5a0bd 100644 --- a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js +++ b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js @@ -290,14 +290,16 @@ function cartToSalesOrder(cart) { let lineItems = []; if (cart.items) { lineItems = cart.items.map((item) => { - return { - number: item._id, - itemCode: item.productId, - quantity: item.quantity, - amount: item.variants.price * item.quantity, - description: item.title, - taxCode: item.variants.taxCode - }; + if (item.taxable) { + return { + number: item._id, + itemCode: item.productId, + quantity: item.quantity, + amount: item.variants.price * item.quantity, + description: item.title, + taxCode: item.variants.taxCode + }; + } }); if (cartShipping) { lineItems.push({ @@ -390,14 +392,16 @@ function orderToSalesInvoice(order) { const orderShipping = order.orderShipping(); const orderDate = moment(order.createdAt).format(); const lineItems = order.items.map((item) => { - return { - number: item._id, - itemCode: item.productId, - quantity: item.quantity, - amount: item.variants.price * item.quantity, - description: item.title, - taxCode: item.variants.taxCode - }; + if (item.taxable) { + return { + number: item._id, + itemCode: item.productId, + quantity: item.quantity, + amount: item.variants.price * item.quantity, + description: item.title, + taxCode: item.variants.taxCode + }; + } }); if (orderShipping) { lineItems.push({ From bd8e03c5bae20377ac9f8a9a7d838a3000950ef3 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Mon, 6 Mar 2017 17:44:59 +0800 Subject: [PATCH 071/120] Move taxDescription down to the variant level and pass if exists --- .../product-admin/client/components/productAdmin.js | 11 ----------- .../variants/variantForm/variantForm.html | 8 ++++++++ .../productDetail/variants/variantForm/variantForm.js | 2 +- .../included/taxes-avalara/server/methods/taxCalc.js | 4 ++-- lib/collections/schemas/products.js | 10 +++++----- 5 files changed, 16 insertions(+), 19 deletions(-) diff --git a/imports/plugins/included/product-admin/client/components/productAdmin.js b/imports/plugins/included/product-admin/client/components/productAdmin.js index 67ec55d1228..980fbab1804 100644 --- a/imports/plugins/included/product-admin/client/components/productAdmin.js +++ b/imports/plugins/included/product-admin/client/components/productAdmin.js @@ -299,17 +299,6 @@ class ProductAdmin extends Component { ref="countryOfOriginInput" value={this.product.originCountry} /> - {{/unless}} + +
+ + {{>afFieldInput name='taxDescription' placeholder=""}} + {{#if afFieldIsInvalid name='taxDescription'}} + {{afFieldMessage name='taxDescription'}} + {{/if}} +
{{>afFieldInput name='inventoryManagement'}} {{#if afFieldIsInvalid name='inventoryManagement'}} diff --git a/imports/plugins/included/product-variant/client/templates/products/productDetail/variants/variantForm/variantForm.js b/imports/plugins/included/product-variant/client/templates/products/productDetail/variants/variantForm/variantForm.js index 306713615c5..5f193800ed2 100644 --- a/imports/plugins/included/product-variant/client/templates/products/productDetail/variants/variantForm/variantForm.js +++ b/imports/plugins/included/product-variant/client/templates/products/productDetail/variants/variantForm/variantForm.js @@ -227,7 +227,7 @@ Template.variantForm.events({ }); } } - } else if (field === "taxCode") { + } else if (field === "taxCode" || field === "taxDescription") { const value = Template.instance().$(event.currentTarget).prop("value"); Meteor.call("products/updateProductField", template.data._id, field, value, error => { diff --git a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js index 978d7d5a0bd..c135ece6658 100644 --- a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js +++ b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js @@ -296,7 +296,7 @@ function cartToSalesOrder(cart) { itemCode: item.productId, quantity: item.quantity, amount: item.variants.price * item.quantity, - description: item.title, + description: item.taxDescription || item.title, taxCode: item.variants.taxCode }; } @@ -398,7 +398,7 @@ function orderToSalesInvoice(order) { itemCode: item.productId, quantity: item.quantity, amount: item.variants.price * item.quantity, - description: item.title, + description: item.taxDescription || item.title, taxCode: item.variants.taxCode }; } diff --git a/lib/collections/schemas/products.js b/lib/collections/schemas/products.js index 962aa3d5504..a98d8d9601f 100644 --- a/lib/collections/schemas/products.js +++ b/lib/collections/schemas/products.js @@ -257,6 +257,11 @@ export const ProductVariant = new SimpleSchema({ defaultValue: "00000", optional: true }, + taxDescription: { + type: String, + optional: true, + label: "Tax Description" + }, // Label for customers title: { label: "Label", @@ -356,11 +361,6 @@ export const Product = new SimpleSchema({ type: String, optional: true }, - taxDescription: { - type: String, - optional: true, - label: "Tax Description" - }, type: { label: "Type", type: String, From c97de619edcbea1e4a7363afa362f13fdaadb6db Mon Sep 17 00:00:00 2001 From: Seun Martins Date: Mon, 6 Mar 2017 11:54:29 +0100 Subject: [PATCH 072/120] Move tax settings form into manage sidebar --- .../templates/dashboard/dashboard.html | 18 ++++------ .../accounts/templates/dashboard/dashboard.js | 20 +++-------- .../accounts/templates/members/member.html | 4 ++- .../accounts/templates/members/member.js | 13 +++++++ .../templates/tax-settings/taxSettings.html | 34 +++++++++---------- .../templates/tax-settings/taxSettings.js | 1 - 6 files changed, 43 insertions(+), 47 deletions(-) diff --git a/client/modules/accounts/templates/dashboard/dashboard.html b/client/modules/accounts/templates/dashboard/dashboard.html index 222396dfc9e..e2b308de3b2 100644 --- a/client/modules/accounts/templates/dashboard/dashboard.html +++ b/client/modules/accounts/templates/dashboard/dashboard.html @@ -12,14 +12,11 @@

    - {{#each accountMember in members}} - {{#if isShopMember accountMember }} + {{#each members}} + {{#if isShopMember}}
  • - {{> member accountMember }} + {{> member}}
  • - {{#if showAvalaraTaxSettings }} - {{> taxSettingsPanel member=accountMember}} - {{/if}} {{/if}} {{/each}}
@@ -33,14 +30,11 @@

    - {{#each guestMember in members}} - {{#if isShopGuest guestMember}} + {{#each members}} + {{#if isShopGuest}}
  • - {{> member guestMember}} + {{> member}}
  • - {{#if showAvalaraTaxSettings }} - {{> taxSettingsPanel member=guestMember }} - {{/if}} {{/if}} {{/each}}
diff --git a/client/modules/accounts/templates/dashboard/dashboard.js b/client/modules/accounts/templates/dashboard/dashboard.js index eba44110383..13482725f33 100644 --- a/client/modules/accounts/templates/dashboard/dashboard.js +++ b/client/modules/accounts/templates/dashboard/dashboard.js @@ -20,8 +20,8 @@ Template.accountsDashboard.helpers({ * @param {Object} member member object * @return {Boolean} True if the memnber is an administrator */ - isShopMember(member) { - return _.includes(["dashboard", "admin", "owner"], member.role); + isShopMember() { + return _.includes(["dashboard", "admin", "owner"], this.role); }, /** @@ -29,20 +29,8 @@ Template.accountsDashboard.helpers({ * @param {Object} member member object * @return {Boolean} True if the member is a guest */ - isShopGuest(member) { - return !_.includes(["dashboard", "admin", "owner"], member.role); - }, - /** - * showAvalaraTaxSettings - * @return {Boolean} True if avalara is enabled. Defaults to false if not found - */ - showAvalaraTaxSettings() { - const avalara = Packages.findOne({ - name: "taxes-avalara", - shopId: Reaction.getShopId() - }); - - return _.get(avalara, "settings.avalara.enabled", false); + isShopGuest() { + return !_.includes(["dashboard", "admin", "owner"], this.role); }, /** * members diff --git a/client/modules/accounts/templates/members/member.html b/client/modules/accounts/templates/members/member.html index a67be7674af..52e3186cb8e 100644 --- a/client/modules/accounts/templates/members/member.html +++ b/client/modules/accounts/templates/members/member.html @@ -31,7 +31,9 @@
- + {{#if showAvalaraTaxSettings }} + {{> taxSettingsPanel member=this }} + {{/if}} {{#each groupsForUser}} {{#if permissionGroups}} diff --git a/client/modules/accounts/templates/members/member.js b/client/modules/accounts/templates/members/member.js index d756d8b6edb..801e7f5b04f 100644 --- a/client/modules/accounts/templates/members/member.js +++ b/client/modules/accounts/templates/members/member.js @@ -1,3 +1,4 @@ +import _ from "lodash"; import { Reaction } from "/client/api"; import { Packages, Shops } from "/lib/collections"; import { Meteor } from "meteor/meteor"; @@ -113,6 +114,18 @@ Template.memberSettings.helpers({ hasManyPermissions: function (permissions) { return Boolean(permissions.length); + }, + /** + * showAvalaraTaxSettings + * @return {Boolean} True if avalara is enabled. Defaults to false if not found + */ + showAvalaraTaxSettings() { + const avalara = Packages.findOne({ + name: "taxes-avalara", + shopId: Reaction.getShopId() + }); + + return _.get(avalara, "settings.avalara.enabled", false); } }); diff --git a/client/modules/accounts/templates/tax-settings/taxSettings.html b/client/modules/accounts/templates/tax-settings/taxSettings.html index 4cfd9288a4b..90cfe66355a 100644 --- a/client/modules/accounts/templates/tax-settings/taxSettings.html +++ b/client/modules/accounts/templates/tax-settings/taxSettings.html @@ -1,21 +1,21 @@ diff --git a/client/modules/accounts/templates/tax-settings/taxSettings.js b/client/modules/accounts/templates/tax-settings/taxSettings.js index a9b456f63ff..9d937711f23 100644 --- a/client/modules/accounts/templates/tax-settings/taxSettings.js +++ b/client/modules/accounts/templates/tax-settings/taxSettings.js @@ -48,7 +48,6 @@ Template.taxSettingsPanel.onCreated(function () { this.autorun(() => { this.subscribe("Account"); }); - // Todo: Fix: currently called for each customer. To be called once Meteor.call("avalara/getEntityCodes", (error, entityCodes) => { const currentCodes = TaxEntityCodes.find().fetch(); if (!currentCodes.length) { From 6c389a57671a9f9f44d4b56295e32af6ed6c6b21 Mon Sep 17 00:00:00 2001 From: Seun Martins Date: Mon, 6 Mar 2017 19:02:03 +0100 Subject: [PATCH 073/120] Fix tax settings saving bug --- .../accounts/templates/tax-settings/taxSettings.js | 9 ++++----- server/publications/collections/accounts.js | 11 +++++++++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/client/modules/accounts/templates/tax-settings/taxSettings.js b/client/modules/accounts/templates/tax-settings/taxSettings.js index 9d937711f23..986f367e73d 100644 --- a/client/modules/accounts/templates/tax-settings/taxSettings.js +++ b/client/modules/accounts/templates/tax-settings/taxSettings.js @@ -1,16 +1,15 @@ import _ from "lodash"; +import { Meteor } from "meteor/meteor"; import { Template } from "meteor/templating"; import { Accounts } from "/lib/collections"; import { Accounts as AccountsSchema } from "/lib/collections/schemas/accounts"; -import { Reaction } from "/client/api"; import { TaxEntityCodes } from "/client/collections"; Template.taxSettingsPanel.helpers({ account() { - if (Reaction.Subscriptions.Account.ready()) { - return Accounts.findOne({ - _id: this.member.userId - }); + const sub = Meteor.subscribe("Accounts.single", this.member.userId); + if (sub.ready()) { + return Accounts.findOne({ _id: this.member.userId }); } return null; }, diff --git a/server/publications/collections/accounts.js b/server/publications/collections/accounts.js index a921a68ca27..c826437237a 100644 --- a/server/publications/collections/accounts.js +++ b/server/publications/collections/accounts.js @@ -44,6 +44,17 @@ Meteor.publish("Accounts", function (userId) { }); }); +/** + * Single account + * @params {String} userId - id of user to find + */ +Meteor.publish("Accounts.single", function (userId) { + check(userId, Match.OneOf(String, null)); + return Collections.Accounts.find({ + userId: userId + }); +}); + /** * userProfile * @deprecated since version 0.10.2 From 9b407ee792f59e072c6d09b6ec2a8e7f357c6a92 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Tue, 7 Mar 2017 10:14:12 +0800 Subject: [PATCH 074/120] Renamed files to fit style guide --- client/collections/index.js | 2 +- client/collections/{tax-entitycodes.js => taxEntitycodes.js} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename client/collections/{tax-entitycodes.js => taxEntitycodes.js} (100%) diff --git a/client/collections/index.js b/client/collections/index.js index ed6a02c7162..811bf1db0b0 100644 --- a/client/collections/index.js +++ b/client/collections/index.js @@ -1,2 +1,2 @@ export * from "./countries"; -export * from "./tax-entitycodes"; +export * from "./taxEntitycodes"; diff --git a/client/collections/tax-entitycodes.js b/client/collections/taxEntitycodes.js similarity index 100% rename from client/collections/tax-entitycodes.js rename to client/collections/taxEntitycodes.js From 54defcf5299e00a80a19617239a07e184a52dd5e Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Tue, 7 Mar 2017 10:17:26 +0800 Subject: [PATCH 075/120] Don't allow a wide-open Account publication --- server/publications/collections/accounts.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/server/publications/collections/accounts.js b/server/publications/collections/accounts.js index c826437237a..ba302fe2dee 100644 --- a/server/publications/collections/accounts.js +++ b/server/publications/collections/accounts.js @@ -50,9 +50,14 @@ Meteor.publish("Accounts", function (userId) { */ Meteor.publish("Accounts.single", function (userId) { check(userId, Match.OneOf(String, null)); - return Collections.Accounts.find({ - userId: userId - }); + + const shopId = Reaction.getShopId(); + if (Roles.userIsInRole(this.userId, ["admin", "owner"], shopId)) { + return Collections.Accounts.find({ + userId: userId + }); + } + return this.ready(); }); /** From b5ff3ca50bbb00a03beecae602f7d01d867b9725 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Tue, 7 Mar 2017 10:18:30 +0800 Subject: [PATCH 076/120] Remove unnecessary subscription --- client/modules/accounts/templates/tax-settings/taxSettings.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/client/modules/accounts/templates/tax-settings/taxSettings.js b/client/modules/accounts/templates/tax-settings/taxSettings.js index 986f367e73d..69e0de47c81 100644 --- a/client/modules/accounts/templates/tax-settings/taxSettings.js +++ b/client/modules/accounts/templates/tax-settings/taxSettings.js @@ -44,9 +44,6 @@ Template.taxSettingsPanel.events({ }); Template.taxSettingsPanel.onCreated(function () { - this.autorun(() => { - this.subscribe("Account"); - }); Meteor.call("avalara/getEntityCodes", (error, entityCodes) => { const currentCodes = TaxEntityCodes.find().fetch(); if (!currentCodes.length) { From 6a4f25fe893c0d6aee263a06cc511fde5a125e44 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Tue, 7 Mar 2017 10:38:19 +0800 Subject: [PATCH 077/120] Don't call API if entity codes is already populated --- .../accounts/templates/tax-settings/taxSettings.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/client/modules/accounts/templates/tax-settings/taxSettings.js b/client/modules/accounts/templates/tax-settings/taxSettings.js index 69e0de47c81..e5591ffd248 100644 --- a/client/modules/accounts/templates/tax-settings/taxSettings.js +++ b/client/modules/accounts/templates/tax-settings/taxSettings.js @@ -44,14 +44,14 @@ Template.taxSettingsPanel.events({ }); Template.taxSettingsPanel.onCreated(function () { - Meteor.call("avalara/getEntityCodes", (error, entityCodes) => { - const currentCodes = TaxEntityCodes.find().fetch(); - if (!currentCodes.length) { + const currentCodes = TaxEntityCodes.find().fetch(); + if (!currentCodes.length) { + Meteor.call("avalara/getEntityCodes", (error, entityCodes) => { _.each(entityCodes, (entityCode) => { TaxEntityCodes.insert(entityCode); }); - } - }); + }); + } }); AutoForm.hooks({ From 970d82b7ee48c08cbee0eb3a58bf1bf97ad3fa42 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Tue, 7 Mar 2017 10:42:36 +0800 Subject: [PATCH 078/120] Simplify logic for returning no results --- imports/plugins/core/logging/server/publications.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/imports/plugins/core/logging/server/publications.js b/imports/plugins/core/logging/server/publications.js index f82c86476cb..c6d7e4f5f75 100644 --- a/imports/plugins/core/logging/server/publications.js +++ b/imports/plugins/core/logging/server/publications.js @@ -15,16 +15,12 @@ Meteor.publish("Logs", function (query, options) { check(query, Match.OneOf(undefined, Object)); check(options, Match.OneOf(undefined, Object)); - if (!query || !query.logType) { - return this.ready(); - } - - const logType = query.logType; const shopId = Reaction.getShopId(); - if (!shopId) { + if (!query || !query.logType || shopId) { return this.ready(); } + const logType = query.logType; if (Roles.userIsInRole(this.userId, ["admin", "owner"])) { Counts.publish(this, "logs-count", Logs.find({ shopId, logType })); return Logs.find({ shopId, logType }, { sort: { date: 1 } }); From fdb9a3102ee52caf78f367f82ae5fe408a06b9d4 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Tue, 7 Mar 2017 10:44:34 +0800 Subject: [PATCH 079/120] Don't select a tax rate but a tax code --- .../products/productDetail/variants/variantForm/variantForm.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imports/plugins/included/product-variant/client/templates/products/productDetail/variants/variantForm/variantForm.js b/imports/plugins/included/product-variant/client/templates/products/productDetail/variants/variantForm/variantForm.js index 5f193800ed2..eb78de87052 100644 --- a/imports/plugins/included/product-variant/client/templates/products/productDetail/variants/variantForm/variantForm.js +++ b/imports/plugins/included/product-variant/client/templates/products/productDetail/variants/variantForm/variantForm.js @@ -31,7 +31,7 @@ Template.variantForm.onCreated(function () { Template.variantForm.onRendered(function () { $("#taxCode").select2({ - placeholder: "Select Tax Rate" + placeholder: "Select Tax Code" }); }); From dca26d7195eb37607542432ad29e10aec0cda6de Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Tue, 7 Mar 2017 10:48:01 +0800 Subject: [PATCH 080/120] Rename function --- .../productDetail/variants/variantForm/variantForm.html | 2 +- .../products/productDetail/variants/variantForm/variantForm.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/imports/plugins/included/product-variant/client/templates/products/productDetail/variants/variantForm/variantForm.html b/imports/plugins/included/product-variant/client/templates/products/productDetail/variants/variantForm/variantForm.html index 3463dafc3c8..d650640d2a5 100644 --- a/imports/plugins/included/product-variant/client/templates/products/productDetail/variants/variantForm/variantForm.html +++ b/imports/plugins/included/product-variant/client/templates/products/productDetail/variants/variantForm/variantForm.html @@ -145,7 +145,7 @@ {{afFieldMessage name='taxable'}} {{/if}}
- {{#unless checkIfProviderIsEnabled}} + {{#unless isProviderIsEnabled}}
{{>afFieldInput name='taxCode'}}
diff --git a/imports/plugins/included/product-variant/client/templates/products/productDetail/variants/variantForm/variantForm.js b/imports/plugins/included/product-variant/client/templates/products/productDetail/variants/variantForm/variantForm.js index eb78de87052..a4cf0a4d6b5 100644 --- a/imports/plugins/included/product-variant/client/templates/products/productDetail/variants/variantForm/variantForm.js +++ b/imports/plugins/included/product-variant/client/templates/products/productDetail/variants/variantForm/variantForm.js @@ -134,7 +134,7 @@ Template.variantForm.helpers({ }; }; }, - checkIfProviderIsEnabled: function () { + isProviderIsEnabled: function () { const shopId = Reaction.getShopId(); const provider = Packages.findOne({ From d420e1ce2aebb6e73f82531e4fc41b36017a84f5 Mon Sep 17 00:00:00 2001 From: Seun Martins Date: Tue, 7 Mar 2017 07:22:33 +0100 Subject: [PATCH 081/120] Properly set Avalara countryList defaultValues --- .../included/taxes-avalara/client/settings/avalara.html | 3 ++- .../included/taxes-avalara/client/settings/avalara.js | 8 ++++++-- .../taxes-avalara/lib/collections/schemas/schema.js | 3 +-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/imports/plugins/included/taxes-avalara/client/settings/avalara.html b/imports/plugins/included/taxes-avalara/client/settings/avalara.html index 407205cdf96..e2088c6b3a8 100644 --- a/imports/plugins/included/taxes-avalara/client/settings/avalara.html +++ b/imports/plugins/included/taxes-avalara/client/settings/avalara.html @@ -17,7 +17,8 @@ {{>afQuickField name='settings.avalara.mode' class='form-control'}} {{>afQuickField name='settings.avalara.performTaxCalculation' class='form-control'}} {{>afQuickField name='settings.addressValidation.enabled' class='form-control'}} - {{>afQuickField name="settings.addressValidation.countryList" type="select-checkbox" options=countryOptions value=country }} + {{>afQuickField name="settings.addressValidation.countryList" + type="select-checkbox" options=countryOptions value=country defaultValue=countryDefaults }} {{>afQuickField name='settings.avalara.enableLogging' class='form-control'}} {{>afQuickField name='settings.avalara.logRetentionDuration' class='form-control'}} {{>afQuickField name='settings.avalara.requestTimeout' class='form-control'}} diff --git a/imports/plugins/included/taxes-avalara/client/settings/avalara.js b/imports/plugins/included/taxes-avalara/client/settings/avalara.js index de801230e94..82f047db17f 100644 --- a/imports/plugins/included/taxes-avalara/client/settings/avalara.js +++ b/imports/plugins/included/taxes-avalara/client/settings/avalara.js @@ -34,6 +34,8 @@ Template.avalaraSettings.onCreated(function () { }); }); +// Avalara supports only Canada and US for address validation +const countryDefaults = ["US", "CA"]; Template.avalaraSettings.helpers({ packageConfigSchema() { @@ -49,8 +51,10 @@ Template.avalaraSettings.helpers({ return Logs; }, countryOptions() { - // Avalara supports only Canada and US for address validation - return Countries.find({ value: { $in: ["US", "CA"] } }).fetch(); + return Countries.find({ value: { $in: countryDefaults } }).fetch(); + }, + countryDefaults() { + return countryDefaults; }, currentCountryList() { return AutoForm.getFieldValue("settings.addressValidation.countryList"); diff --git a/imports/plugins/included/taxes-avalara/lib/collections/schemas/schema.js b/imports/plugins/included/taxes-avalara/lib/collections/schemas/schema.js index 233f13059c5..25470182de8 100644 --- a/imports/plugins/included/taxes-avalara/lib/collections/schemas/schema.js +++ b/imports/plugins/included/taxes-avalara/lib/collections/schemas/schema.js @@ -74,8 +74,7 @@ export const AvalaraPackageConfig = new SimpleSchema([ "settings.addressValidation.countryList": { label: "Enable Address Validation by Country", type: [String], - optional: true, - defaultValue: ["US", "CA"] + optional: true } } ]); From eea4cb9b14e2315a297005ec23f0896103226297 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Wed, 8 Mar 2017 05:25:11 +0800 Subject: [PATCH 082/120] Eliminate extra slash --- .../plugins/included/taxes-avalara/server/methods/taxCalc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js index c135ece6658..933bf04c2fb 100644 --- a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js +++ b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js @@ -214,7 +214,7 @@ taxCalc.validateAddress = function (address) { addressToValidate.line2 = address.address2; } const baseUrl = getUrl(); - const requestUrl = `${baseUrl}/addresses/resolve`; + const requestUrl = `${baseUrl}addresses/resolve`; const result = avaPost(requestUrl, { data: addressToValidate }); let content; From 764ef901fc58b1aa2a7e938b3d3ed96b86a14c6b Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Wed, 8 Mar 2017 05:56:34 +0800 Subject: [PATCH 083/120] Eliminate all extra slashes --- .../included/taxes-avalara/server/methods/taxCalc.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js index 933bf04c2fb..d45b3c77586 100644 --- a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js +++ b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js @@ -366,7 +366,7 @@ taxCalc.estimateCart = function (cart, callback) { if (cart.items && cart.shipping && cart.shipping[0].address) { const salesOrder = _.assign({}, cartToSalesOrder(cart), getTaxSettings(cart.userId)); const baseUrl = getUrl(); - const requestUrl = `${baseUrl}/transactions/create`; + const requestUrl = `${baseUrl}transactions/create`; const result = avaPost(requestUrl, { data: salesOrder }); return callback(result.data); } @@ -465,7 +465,7 @@ taxCalc.recordOrder = function (order, callback) { if (order && order.shipping && order.shipping[0].address) { const salesOrder = _.assign({}, orderToSalesInvoice(order), getTaxSettings(order.userId)); const baseUrl = getUrl(); - const requestUrl = `${baseUrl}/transactions/create`; + const requestUrl = `${baseUrl}transactions/create`; try { const result = avaPost(requestUrl, { data: salesOrder }); return callback(result.data); @@ -492,7 +492,7 @@ taxCalc.reportRefund = function (order, refundAmount, callback) { const companyShipping = _.filter(company.addressBook, (o) => o.isShippingDefault)[0]; const currencyCode = company.currency; const baseUrl = getUrl(); - const requestUrl = `${baseUrl}/transactions/create`; + const requestUrl = `${baseUrl}transactions/create`; const returnAmount = refundAmount * -1; const orderDate = moment(order.createdAt).format(); const refundDate = moment().format(); From 26dc92abc94b32160a6da5705647dd27a8740460 Mon Sep 17 00:00:00 2001 From: Seun Martins Date: Tue, 7 Mar 2017 23:30:21 +0100 Subject: [PATCH 084/120] Check to ensure data is returned from API --- .../plugins/included/taxes-avalara/server/methods/taxCalc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js index d45b3c77586..3f0a7d913bc 100644 --- a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js +++ b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js @@ -223,7 +223,7 @@ taxCalc.validateAddress = function (address) { } catch (error) { content = result.content; } - if (content.messages) { + if (content && content.messages) { messages = content.messages; } if (messages) { From 3ff33c9600aa4f301f00bb52bec8a31456c83bc5 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Wed, 8 Mar 2017 08:49:06 +0800 Subject: [PATCH 085/120] Also log errors in Avalogger. Correctly filter for taxable items. --- .../included/taxes-avalara/server/methods/taxCalc.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js index 3f0a7d913bc..7e029fa2022 100644 --- a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js +++ b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js @@ -94,6 +94,8 @@ function avaGet(requestUrl, options = {}) { result = error; Logger.error(`Encountered error while calling Avalara API endpoint ${requestUrl}`); Logger.error(error); + logObject.error = error; + Avalogger.info(logObject); } if (pkgData.settings.avalara.enableLogging) { @@ -139,6 +141,8 @@ function avaPost(requestUrl, options) { } catch (error) { Logger.error(`Encountered error while calling API at ${requestUrl}`); Logger.error(error); + logObject.error = error; + Avalogger.info(logObject); result = {}; } @@ -290,7 +294,7 @@ function cartToSalesOrder(cart) { let lineItems = []; if (cart.items) { lineItems = cart.items.map((item) => { - if (item.taxable) { + if (item.variants.taxable) { return { number: item._id, itemCode: item.productId, @@ -392,7 +396,7 @@ function orderToSalesInvoice(order) { const orderShipping = order.orderShipping(); const orderDate = moment(order.createdAt).format(); const lineItems = order.items.map((item) => { - if (item.taxable) { + if (item.variants.taxable) { return { number: item._id, itemCode: item.productId, From 9bda0d2f44a654c190fd78c9f35d3ef419d5be31 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Wed, 8 Mar 2017 11:13:53 +0800 Subject: [PATCH 086/120] Require shopId --- imports/plugins/core/logging/server/publications.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imports/plugins/core/logging/server/publications.js b/imports/plugins/core/logging/server/publications.js index c6d7e4f5f75..22097c1674c 100644 --- a/imports/plugins/core/logging/server/publications.js +++ b/imports/plugins/core/logging/server/publications.js @@ -16,7 +16,7 @@ Meteor.publish("Logs", function (query, options) { check(options, Match.OneOf(undefined, Object)); const shopId = Reaction.getShopId(); - if (!query || !query.logType || shopId) { + if (!query || !query.logType || !shopId) { return this.ready(); } From e3ebcc8eb06dd94c6c9a1242fd71aa426cda1e7f Mon Sep 17 00:00:00 2001 From: Erik Kieckhafer Date: Tue, 7 Mar 2017 22:46:16 -0800 Subject: [PATCH 087/120] linting fixes --- .../client/containers/publishContainer.js | 2 +- .../client/settings/avagriddle.js | 26 ++++++++++--------- lib/collections/collections.js | 2 +- lib/collections/schemas/products.js | 1 - server/api/core/core.js | 1 - server/publications/email.js | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/imports/plugins/core/revisions/client/containers/publishContainer.js b/imports/plugins/core/revisions/client/containers/publishContainer.js index 915c32f9dc6..9a5c688dee0 100644 --- a/imports/plugins/core/revisions/client/containers/publishContainer.js +++ b/imports/plugins/core/revisions/client/containers/publishContainer.js @@ -31,7 +31,7 @@ class PublishContainer extends Component { Alerts.toast(message, "success"); if (this.props.onPublishSuccess) { - this.props.onPublishSuccess(result) + this.props.onPublishSuccess(result); } } else { const message = i18next.t("revisions.noChangesPublished", { diff --git a/imports/plugins/included/taxes-avalara/client/settings/avagriddle.js b/imports/plugins/included/taxes-avalara/client/settings/avagriddle.js index f385c8e0ea0..82fc37e00d1 100644 --- a/imports/plugins/included/taxes-avalara/client/settings/avagriddle.js +++ b/imports/plugins/included/taxes-avalara/client/settings/avagriddle.js @@ -57,18 +57,20 @@ const LogGriddle = React.createClass({ const maxPages = Math.ceil(this.data.matchingResults / this.state.externalResultsPerPage); const allProps = this.props; - return (); + return ( + + ); } }); diff --git a/lib/collections/collections.js b/lib/collections/collections.js index 54ec1b3353e..d83e8710d06 100644 --- a/lib/collections/collections.js +++ b/lib/collections/collections.js @@ -71,7 +71,7 @@ export const Orders = new Mongo.Collection("Orders", { transform(order) { const newInstance = Object.create(orderTransform); return _.extend(newInstance, order); - } + } }); Orders.attachSchema([ diff --git a/lib/collections/schemas/products.js b/lib/collections/schemas/products.js index b3044598df8..15ffbf24275 100644 --- a/lib/collections/schemas/products.js +++ b/lib/collections/schemas/products.js @@ -300,7 +300,6 @@ export const ProductVariant = new SimpleSchema({ type: Workflow, optional: true }, - originCountry: { type: String, optional: true diff --git a/server/api/core/core.js b/server/api/core/core.js index eafce91cf26..b37ba650e8f 100644 --- a/server/api/core/core.js +++ b/server/api/core/core.js @@ -12,7 +12,6 @@ import { sendVerificationEmail } from "./accounts"; import { getMailUrl } from "./email/config"; - export default { init() { diff --git a/server/publications/email.js b/server/publications/email.js index 5f238b781b8..8bee9c50545 100644 --- a/server/publications/email.js +++ b/server/publications/email.js @@ -9,7 +9,7 @@ import { Roles } from "meteor/alanning:roles"; Meteor.publish("Emails", function (query, options) { check(query, Match.Optional(Object)); check(options, Match.Optional(Object)); - + if (Roles.userIsInRole(this.userId, ["owner", "admin", "dashboard"])) { Counts.publish(this, "emails-count", Jobs.find({ type: "sendEmail" })); return Jobs.find({ type: "sendEmail" }); From fe64d503f6753a316e7fc8f151f853e98f535256 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Wed, 8 Mar 2017 14:02:17 +0800 Subject: [PATCH 088/120] Add label to plain select. Rename method --- .../productDetail/variants/variantForm/variantForm.html | 3 ++- .../products/productDetail/variants/variantForm/variantForm.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/imports/plugins/included/product-variant/client/templates/products/productDetail/variants/variantForm/variantForm.html b/imports/plugins/included/product-variant/client/templates/products/productDetail/variants/variantForm/variantForm.html index d650640d2a5..37837398975 100644 --- a/imports/plugins/included/product-variant/client/templates/products/productDetail/variants/variantForm/variantForm.html +++ b/imports/plugins/included/product-variant/client/templates/products/productDetail/variants/variantForm/variantForm.html @@ -145,8 +145,9 @@ {{afFieldMessage name='taxable'}} {{/if}} - {{#unless isProviderIsEnabled}} + {{#unless isProviderEnabled}}
+ {{>afFieldInput name='taxCode'}}
{{else}} diff --git a/imports/plugins/included/product-variant/client/templates/products/productDetail/variants/variantForm/variantForm.js b/imports/plugins/included/product-variant/client/templates/products/productDetail/variants/variantForm/variantForm.js index a4cf0a4d6b5..33194de2a26 100644 --- a/imports/plugins/included/product-variant/client/templates/products/productDetail/variants/variantForm/variantForm.js +++ b/imports/plugins/included/product-variant/client/templates/products/productDetail/variants/variantForm/variantForm.js @@ -134,7 +134,7 @@ Template.variantForm.helpers({ }; }; }, - isProviderIsEnabled: function () { + isProviderEnabled: function () { const shopId = Reaction.getShopId(); const provider = Packages.findOne({ From b949d4af21672897e4c636c51d7f427061361199 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Wed, 8 Mar 2017 16:32:13 +0800 Subject: [PATCH 089/120] JSDoc linting --- imports/plugins/included/taxes-avalara/server/jobs/cleanup.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imports/plugins/included/taxes-avalara/server/jobs/cleanup.js b/imports/plugins/included/taxes-avalara/server/jobs/cleanup.js index f773f0e3c22..86d7ccb5fd2 100644 --- a/imports/plugins/included/taxes-avalara/server/jobs/cleanup.js +++ b/imports/plugins/included/taxes-avalara/server/jobs/cleanup.js @@ -7,7 +7,8 @@ import taxCalc from "../methods/taxCalc"; /** * @summary Remove logs older than the configured number of days - * @returns results of remmoval query + * @param {Function} callback - function to call when process complete + * @returns {Number} results of remmoval query */ function cleanupAvalaraJobs(callback) { const pkgData = taxCalc.getPackageData(); From 376d26d99d3c70db9285a8a43c3dc82237442324 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Wed, 8 Mar 2017 16:34:01 +0800 Subject: [PATCH 090/120] Remove unused import --- client/modules/accounts/templates/dashboard/dashboard.js | 1 - 1 file changed, 1 deletion(-) diff --git a/client/modules/accounts/templates/dashboard/dashboard.js b/client/modules/accounts/templates/dashboard/dashboard.js index 13482725f33..3771052753b 100644 --- a/client/modules/accounts/templates/dashboard/dashboard.js +++ b/client/modules/accounts/templates/dashboard/dashboard.js @@ -3,7 +3,6 @@ import { Meteor } from "meteor/meteor"; import { Template } from "meteor/templating"; import { Reaction, i18next } from "/client/api"; import { ServiceConfigHelper } from "../../helpers/util"; -import { Packages } from "/lib/collections"; /** * Accounts helpers From e75a8131ca7e9b85322b99210e72c14ac9832aa7 Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Wed, 8 Mar 2017 16:35:47 +0800 Subject: [PATCH 091/120] Remove unused import --- imports/plugins/core/email/client/actions/logs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imports/plugins/core/email/client/actions/logs.js b/imports/plugins/core/email/client/actions/logs.js index eb50edc1018..f387e3eee90 100644 --- a/imports/plugins/core/email/client/actions/logs.js +++ b/imports/plugins/core/email/client/actions/logs.js @@ -1,4 +1,4 @@ -import { Router, i18next } from "/client/api"; +import { i18next } from "/client/api"; export default { /** From 562d8ad038448d00cf251ede1d79ad54aeb63ede Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Wed, 8 Mar 2017 16:37:21 +0800 Subject: [PATCH 092/120] Linting --- imports/plugins/core/email/client/components/emailSettings.js | 2 +- imports/plugins/included/sms/client/components/smsSettings.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/imports/plugins/core/email/client/components/emailSettings.js b/imports/plugins/core/email/client/components/emailSettings.js index e3c4ceefee7..6b022b2a461 100644 --- a/imports/plugins/core/email/client/components/emailSettings.js +++ b/imports/plugins/core/email/client/components/emailSettings.js @@ -31,7 +31,7 @@ class EmailSettings extends Component { handleSelect(e) { const { settings } = this.state; - settings["service"] = e; + settings.service = e; this.setState({ settings }); } diff --git a/imports/plugins/included/sms/client/components/smsSettings.js b/imports/plugins/included/sms/client/components/smsSettings.js index 2a708fc94a1..880254d5556 100644 --- a/imports/plugins/included/sms/client/components/smsSettings.js +++ b/imports/plugins/included/sms/client/components/smsSettings.js @@ -30,7 +30,7 @@ class SmsSettings extends Component { handleSelect(e) { const { settings } = this.state; - settings["smsProvider"] = e; + settings.smsProvider = e; this.setState({ settings }); } From 10d53fb7996637ead11ca381719050299ef2fddb Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Thu, 9 Mar 2017 10:33:59 +0800 Subject: [PATCH 093/120] Check configuration and throw meaningful errors if not configured properly --- .../taxes-avalara/server/methods/taxCalc.js | 58 ++++++++++++++----- 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js index 7e029fa2022..2958f2cca4e 100644 --- a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js +++ b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js @@ -10,6 +10,7 @@ import { Reaction, Logger } from "/server/api"; import Avalogger from "./avalogger"; const countriesWithRegions = ["US", "CA", "DE", "AU"]; +const requiredFields = ["username", "password", "apiLoginId", "companyCode", "shippingTaxCode"]; const taxCalc = {}; taxCalc.getPackageData = function () { @@ -39,18 +40,40 @@ function getUrl() { return baseUrl; } +/** + * @summary Verify that we have all required configuration data before attempting to use the API + * @param {Object} packageData - Package data retrieved from the database + * @returns {boolean} - isValid Is the current configuration valid + */ +function checkConfiguration(packageData = taxCalc.getPackageData()) { + let isValid = true; + const settings = _.get(packageData, "settings.avalara", {}); + for (const field of requiredFields) { + if (!settings[field]) { + const msg = `The Avalara package cannot function unless ${field} is configured`; + Logger.fatal(msg); + Avalogger.info({ error: msg }); + isValid = false; + } + } + if (!isValid) { + throw new Meteor.Error("The Avalara package is not configured correctly. Cannot continue"); + } + return isValid; +} + /** * @summary Get the auth info to authenticate to REST API * @param {Object} packageData - Optionally pass in packageData if we already have it * @returns {String} Username/Password string */ function getAuthData(packageData = taxCalc.getPackageData()) { - const { username, password } = _.get(packageData, "settings.avalara", {}); - if (!username || !password) { - return new Meteor.Error("You cannot use this API without a username and password configured"); + if (checkConfiguration(packageData)) { + const settings = _.get(packageData, "settings.avalara", {}); + const { username, password } = settings; + const auth = `${username}:${password}`; + return auth; } - const auth = `${username}:${password}`; - return auth; } /** @@ -71,6 +94,9 @@ function getTaxSettings(userId) { function avaGet(requestUrl, options = {}) { const logObject = {}; const pkgData = taxCalc.getPackageData(); + if (!checkConfiguration(pkgData)) { + return undefined; + } const appVersion = Reaction.getAppVersion(); const meteorVersion = _.split(Meteor.release, "@")[1]; const machineName = os.hostname(); @@ -160,10 +186,13 @@ function avaPost(requestUrl, options) { * @returns {Object[]} API response */ taxCalc.getEntityCodes = function () { - const baseUrl = getUrl(); - const requestUrl = `${baseUrl}definitions/entityusecodes`; - const result = avaGet(requestUrl, { timeout: 5000 }); - return _.get(result, "data.value", []); + if (checkConfiguration()) { + const baseUrl = getUrl(); + const requestUrl = `${baseUrl}definitions/entityusecodes`; + const result = avaGet(requestUrl, { timeout: 5000 }); + return _.get(result, "data.value", []); + } + throw new Meteor.Error("bad-configuration", "Avalara package is enabled, but is not properly configured"); }; // API Methods @@ -272,10 +301,13 @@ taxCalc.testCredentials = function (credentials) { * @returns {Array} An array of Tax code objects */ taxCalc.getTaxCodes = function () { - const baseUrl = getUrl(); - const requestUrl = `${baseUrl}definitions/taxcodes`; - const result = avaGet(requestUrl); - return _.get(result, "data.value", []); + if (checkConfiguration()) { + const baseUrl = getUrl(); + const requestUrl = `${baseUrl}definitions/taxcodes`; + const result = avaGet(requestUrl); + return _.get(result, "data.value", []); + } + throw new Meteor.Error("bad-configuration", "Avalara Tax package is enabled but not properly configured"); }; /** From 7cfb019c5670269f10ec375308e01af2b3324eaa Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Thu, 9 Mar 2017 11:31:53 +0800 Subject: [PATCH 094/120] Add level to log so we can filter for errors --- .../taxes-avalara/server/methods/avalogger.js | 15 ++++++++++++++- .../taxes-avalara/server/methods/taxCalc.js | 5 +++-- lib/collections/schemas/logs.js | 5 +++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/imports/plugins/included/taxes-avalara/server/methods/avalogger.js b/imports/plugins/included/taxes-avalara/server/methods/avalogger.js index 0daf10df340..7151af94b0f 100644 --- a/imports/plugins/included/taxes-avalara/server/methods/avalogger.js +++ b/imports/plugins/included/taxes-avalara/server/methods/avalogger.js @@ -8,7 +8,20 @@ class BunyanMongo {} BunyanMongo.prototype.write = Meteor.bindEnvironment((logData) => { - const avalog = { logType: "avalara", shopId: Reaction.getShopId(), data: logData }; + const levelToName = { + 10: "trace", + 20: "debug", + 30: "info", + 40: "warn", + 50: "error", + 60: "fatal" + }; + const avalog = { + logType: "avalara", + shopId: Reaction.getShopId(), + data: logData, + level: levelToName[logData.level] + }; Logs.insert(avalog); }); diff --git a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js index 2958f2cca4e..f338dd92f16 100644 --- a/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js +++ b/imports/plugins/included/taxes-avalara/server/methods/taxCalc.js @@ -121,7 +121,7 @@ function avaGet(requestUrl, options = {}) { Logger.error(`Encountered error while calling Avalara API endpoint ${requestUrl}`); Logger.error(error); logObject.error = error; - Avalogger.info(logObject); + Avalogger.error(logObject); } if (pkgData.settings.avalara.enableLogging) { @@ -168,7 +168,8 @@ function avaPost(requestUrl, options) { Logger.error(`Encountered error while calling API at ${requestUrl}`); Logger.error(error); logObject.error = error; - Avalogger.info(logObject); + // whether logging is enabled or not we log out errors + Avalogger.error(logObject); result = {}; } diff --git a/lib/collections/schemas/logs.js b/lib/collections/schemas/logs.js index e5469194b17..cfbc524276a 100644 --- a/lib/collections/schemas/logs.js +++ b/lib/collections/schemas/logs.js @@ -8,6 +8,11 @@ export const Logs = new SimpleSchema({ shopId: { type: String }, + level: { + type: String, + defaultValue: "info", + allowedValues: ["trace", "debug", "info", "warn", "error", "fatal"] + }, data: { type: Object, blackbox: true From 2f084d17617f24f8d4ad7e7c8a2081b73f81a37b Mon Sep 17 00:00:00 2001 From: Brent Hoover Date: Thu, 9 Mar 2017 14:48:13 +0800 Subject: [PATCH 095/120] Tweak HTML and styling in settings panel --- .../included/taxes-avalara/client/index.js | 1 + .../client/settings/avalara.html | 61 +++++++++++++------ .../taxes-avalara/client/styles/settings.less | 7 +++ 3 files changed, 49 insertions(+), 20 deletions(-) create mode 100644 imports/plugins/included/taxes-avalara/client/styles/settings.less diff --git a/imports/plugins/included/taxes-avalara/client/index.js b/imports/plugins/included/taxes-avalara/client/index.js index e0102291566..5f4e16aee4e 100644 --- a/imports/plugins/included/taxes-avalara/client/index.js +++ b/imports/plugins/included/taxes-avalara/client/index.js @@ -1,2 +1,3 @@ import "./settings/avalara.html"; import "./settings/avalara.js"; +import "./styles/settings.less"; diff --git a/imports/plugins/included/taxes-avalara/client/settings/avalara.html b/imports/plugins/included/taxes-avalara/client/settings/avalara.html index e2088c6b3a8..b670e09a6d6 100644 --- a/imports/plugins/included/taxes-avalara/client/settings/avalara.html +++ b/imports/plugins/included/taxes-avalara/client/settings/avalara.html @@ -1,12 +1,4 @@