Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add default parcel size setting #3193

Closed
wants to merge 40 commits into from
Closed

Add default parcel size setting #3193

wants to merge 40 commits into from

Conversation

efalayi
Copy link
Contributor

@efalayi efalayi commented Oct 30, 2017

Resolves #3019
Add a settings panel within the shipping dashboard that permits defining default parcel sizes for products and variants that don't have dimensions defined. Also include sensible defaults for the default parcel size so that it's never null which could cause Shippo or other shipment providers to fail to calculate shipping correctly.

Fix
  • Create component and container for new setting
  • Create new package reaction-shipping-parcel-size to manage parcel size setting
  • Add default values. Reference: USPS
  • Update Shops.json with default parcel size values
  • Update Shops and Product Variant schemas
  • Update core translation
  • Write unit tests for new setting
  • Update cart-create.app.test

Test

  • Reset and start reaction
  • Run meteor mongo and 'use meteor in terminal (or start Robomongo)
  • db.Shops.find({}, {"defaultParcelSize": 1, "_id": 0}) should give { "defaultParcelSize" : { "weight" : 7, "length" : 11.25, "width" : 8.75, "height" : 6 } }
  • Login as admin and enable payment
  • Go to shipping setting
  • Enable Shippo (refresh methods if no values are shown)
  • Check values shown in Default parcel size form fields. They should be the same values in the database
  • Add a product
  • Check the product dimensions. They should be the same values in the database
  • Publish product, add to cart, and checkout (Shipping rates should be displayed)
  • Check Orders in database. items.parcel should be the same value of product dimensions
  • Change product dimensions and repeat step 10.
  • Check Orders in database and verify that items.parcel is equal to the new dimensions saved for the product
  • Create another product and add a variant option. Variant dimensions should be equal to the values in the database
  • Publish product, add variant option to cart, and checkout
  • Check Orders in database. items.parcel should be the same value of variant option dimensions
  • Change variant option dimensions and repeat steps 15 - 16
  • Add a new product and edit dimension values
  • Repeat steps 10 - 13
  • Add a new product
  • Repeat step 18 with zero dimension values (i.e: weight = 0, length = 0, width = 0, height = 0)
  • Check Orders in database and verify that items.parcel is equal to the value for default parcel size
  • Remove defaultParcelSize value in Shops.json
  • Reset and run reaction
  • Repeat above steps

Esther Falayi added 6 commits October 24, 2017 23:00
- Create react component and container
- Create a method for parcel-dimension
- Set deafult parcel dimension sin cart.js
- Add JSDoc
- Display toast message
- Get default parcel size
@rymorgan
Copy link
Contributor

rymorgan commented Oct 30, 2017

@zenweasel Should we take the extra time and convert this to React? Also, based on the title I don't think it's clear what this panel is meant to do. In terms of translations, this is too long but it should be something more like "Default parcel size."

@spencern Do you have any ideas of a shorter clearer way to say that?

@rymorgan
Copy link
Contributor

Also, translations aren't working for the title.

shipping panel

@spencern
Copy link
Contributor

One note based on that screen shot is that I don't think parcel size should have an "enable/disable" switch on it.

Also have failing tests and a failing bithound file

Esther Falayi added 4 commits October 31, 2017 21:06
- Validate input
- Refactor code
- Remove id check
- Remove toggle button
- Update translation
- Update product schema
- Update validation
@efalayi
Copy link
Contributor Author

efalayi commented Nov 2, 2017

@rymorgan I've fixed the translation issue. It now works.

@efalayi efalayi changed the title [WIP] Add default parcel size setting Add default parcel size setting Nov 3, 2017
@efalayi efalayi changed the title Add default parcel size setting [WIP] Add default parcel size setting Nov 3, 2017
@rymorgan
Copy link
Contributor

rymorgan commented Nov 7, 2017

  • Translation still not working.
  • It needs to be sentence case.
  • I think this should say "Default parcel size" to make it's functionality clear. @spencern Can you confirm that that makes the most sense. Parcel size doesn't really say what it does.

basic_reaction_product_and_cart_checkout

@rymorgan
Copy link
Contributor

rymorgan commented Nov 7, 2017

  • Labels are screwed up. Weight says inches and width says lbs so they have the wrong unit of measure.
  • The units of measure shouldn't be hardcoded and it seems like it might be.
  • Remove the save button, it's unnecessary here.

basic_reaction_product-2

@rymorgan
Copy link
Contributor

rymorgan commented Nov 7, 2017

This should be 7 not 70

basic_reaction_product-3

@rymorgan
Copy link
Contributor

rymorgan commented Nov 7, 2017

The weight isn't right on the product level.

basic_reaction_product-4

@efalayi
Copy link
Contributor Author

efalayi commented Nov 7, 2017

@rymorgan the translation files have to be added in /imports/plugins/included/default-parcel-size/server/i18n and updated /imports/plugins/included/default-parcel-size/server/i18n/index.js

@efalayi
Copy link
Contributor Author

efalayi commented Jan 6, 2018

@Akarshit are you working on this presently?

@brent-hoover
Copy link
Collaborator

@efalayi The value is requiresShipping not requiringShipping

requiresShipping: {

@efalayi
Copy link
Contributor Author

efalayi commented Jan 6, 2018

@zenweasel yeah. That was a typo. It's undefined

@brent-hoover
Copy link
Collaborator

2018-01-06_16-30-46

@efalayi
Copy link
Contributor Author

efalayi commented Jan 6, 2018

@zenweasel could you try creating a new product and the check the database?

@brent-hoover
Copy link
Collaborator

2018-01-06_16-43-34

@Akarshit
Copy link
Contributor

Akarshit commented Jan 7, 2018

@efalayi Ya I will be working on this.

@Akarshit
Copy link
Contributor

Akarshit commented Jan 7, 2018

Steps I followed for testing

  1. reaction reset
  2. reaction

Testing default value.

  1. Run 'meteor mongo and 'use meteor in terminal
  2. db.Shops.find({}, {"defaultParcelSize": 1, "_id": 0}) should give { "defaultParcelSize" : { "weight" : 7, "length" : 11.25, "width" : 8.75, "height" : 6 } }
  3. Login as admin
  4. Go to example product and click on the variant, its dimensions should not be the default ones.
  5. Add a product.
  6. Check its variant's dimension, they should be the default ones.
  7. Publish this and buy this.
  8. Check the parcel size in the Orders (db.Orders.find({}, { 'items.parcel': 1, _id: 0 })), should give the default parcel size.
  9. Update the parcel size of the newly added product manually, and again buy the product and check the size in Orders
  10. Change the defaultParcelSize in the Shipping side menu and then repeat the above steps to check consistency.

*/
weight: function () {
const shop = new shopDefaultParcelSize.currentShop();
if (shop && shop.defaultParcelSize) {
Copy link
Contributor

Choose a reason for hiding this comment

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

@zenweasel could this be false anywhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@Akarshit yes. It could be false if defaultParcelSize is not defined in Shops.json. After a discussion with @zenweasel we decided to also set autoValues for defaultParcelSize in shops.js.


// Check if any parcel property is set to zero and set to corresponding value on default parcel size
Object.keys(parcel).forEach(function (key) {
if (parcel[key] === 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

How can this be when in the schema it's defined to have minimum value of 1?

@Akarshit
Copy link
Contributor

Akarshit commented Jan 8, 2018

@efalayi Sorry for the confusion. I am not working on this, please continue.

@brent-hoover
Copy link
Collaborator

@efalayi Before I am going to willing to approve this PR I really need to see some in-depth test instructions that cover all the various aspects and scenarios covered by this ticket (no values in Shop.json,etc. and non-US UOM). This has the potential to introduce critical bugs into checkout so I need to have some really good means for understanding when this is actually working correctly and not having an impact on anything else. Something along the lines of what @Akarshit had started there

brent-hoover and others added 3 commits January 10, 2018 10:08
- Customise error messages
- Refactor autoValue helper function
- Allow zero value for product dimensions
- Add autoValue for shop defaultParceSize
- Fix bug in cart.js
@efalayi
Copy link
Contributor Author

efalayi commented Jan 10, 2018

@zenweasel I just updated the branch.

Test Steps

  1. Reset and start reaction
  2. Run meteor mongo and 'use meteor in terminal (or start Robomongo)
  3. db.Shops.find({}, {"defaultParcelSize": 1, "_id": 0}) should give { "defaultParcelSize" : { "weight" : 7, "length" : 11.25, "width" : 8.75, "height" : 6 } }
  4. Login as admin and enable payment
  5. Go to shipping setting
  6. Enable Shippo (refresh methods if no values are shown)
  7. Check values shown in Default parcel size form fields. They should be the same values in the database
  8. Add a product
  9. Check the product dimensions. They should be the same values in the database
  10. Publish product, add to cart, and checkout (Shipping rates should be displayed)
  11. Check Orders in database. items.parcel should be the same value of product dimensions
  12. Change product dimensions and repeat step 10.
  13. Check Orders in database and verify that items.parcel is equal to the new dimensions saved for the product
  14. Create another product and add a variant option. Variant dimensions should be equal to the values in the database
  15. Publish product, add variant option to cart, and checkout
  16. Check Orders in database. items.parcel should be the same value of variant option dimensions
  17. Change variant option dimensions and repeat steps 15 - 16
  18. Add a new product and edit dimension values
  19. Repeat steps 10 - 13
  20. Add a new product
  21. Repeat step 18 with zero dimension values (i.e: weight = 0, length = 0, width = 0, height = 0)
  22. Check Orders in database and verify that items.parcel is equal to the value for default parcel size
  23. Remove defaulParcelSize value in Shops.json
  24. Reset and run reaction
  25. Repeat above steps
Observation

Some shipping providers have max values for parcel dimensions (for instance DHL).

Suggestions
  • To ensure that users enter the right values, a chart for UOM conversion could be provided
  • For downloadable products with zero dimension values, an option could be provided to update requiresShipping. Since these products do not need to be shipped, requiresShipping would be set to false and users can checkout (without selecting shipping rates). However for now, products with zero dimension values set to defaultParcelSize value.

@Akarshit could you please help test this branch. Thank you.

Copy link
Contributor

@aldeed aldeed left a comment

Choose a reason for hiding this comment

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

See my inline comments. Also, I didn't see tests to make sure that default values are NOT used when individual product values are provided. But maybe an existing test covers that.

One big picture question: Shouldn't they all be optional: true in the schema? What if I wanted to provide a default weight only, but leave the others blank? I don't think the form will be valid without all 4 props having a value.

this.handleFieldFocus = this.handleFieldFocus.bind(this);
this.handleStateChange = this.handleStateChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleCardExpand = this.handleCardExpand.bind(this);
Copy link
Contributor

Choose a reason for hiding this comment

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

All event handlers should be defined as arrow functions the way handleCardExpand is. Then you can delete all of these .bind calls from the constructor.

Also use props rather than this.props everywhere in the constructor.

/**
* @name handleFieldFocus()
* @summary Handle input field focus in form
* @return {function} state for field value
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove @return line from comment. It is not returning anything.

* @name handleStateChange()
* @summary Handle input field change
* @param {script} event - onChange event when typing in input field
* @return {function} state for field value
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove @return line from comment. It is not returning anything.

event.preventDefault();
const { size } = this.state;
const shopId = Reaction.getShopId();
this.setState({ isSaving: true });
Copy link
Contributor

Choose a reason for hiding this comment

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

Although you are calling setState here, no state changes ever happen until this function returns, which means that in an error case, this line is unnecessary.

I would rewrite the logic more like this:

handleSubmit: (event) => {
  event.preventDefault();
  const { saveDefaultSize, validation } = this.props;
  const { size } = this.state;

  const validationStatus = validation().validate(size);
  if (!validationStatus.isValid) {
    this.setState({ validationStatus });
    return;
  }

  this.setState({ isSaving: true });
  saveDefaultSize(Reaction.getShopId(), size, () => {
    this.setState({
      isSaving: false,
      validationStatus: {}
    });
  });
};

* @return {Function} state for field value
*/
handleCardExpand = (event, card, cardName, isExpanded) => {
if (this.props.onCardExpand) {
Copy link
Contributor

Choose a reason for hiding this comment

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

You should destructure props at the top

const { onCardExpand, getEditFocus } = this.props;

"defaultParcelSize.length": size.length,
"defaultParcelSize.width": size.width,
"defaultParcelSize.height": size.height
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not just

$set: { defaultParcelSize: size }

if (error) {
throw new Meteor.Error("server-error", error.message);
}
});
Copy link
Contributor

Choose a reason for hiding this comment

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

It's not good for a method to throw after already returning. You should remove the callback and wrap in a try/catch instead.

shopId = Reaction.getShopId();
} else {
shopId = Reaction.getPrimaryShopId();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I see this same code in 3 places already. Create a helper function they can all share:

function getShopIdCheckingMerchantCart() {
  const marketplaceSettings = Reaction.getMarketplaceSettings();
  if (marketplaceSettings && marketplaceSettings.public && marketplaceSettings.public.merchantCart) {
    return Reaction.getShopId();
  }

  return Reaction.getPrimaryShopId();
}

decimal: true,
defaultValue: 6
}
});
Copy link
Contributor

Choose a reason for hiding this comment

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

When new SimpleSchema is merged, will need to remove decimal: true options.

Also add min: 0 option to all of them

I don't understand why the defaultValues are set as they are. I think this would confuse people. I'd set them all to 0, which will keep things unchanged from how it currently works

weight: getDefaultParcelSize().weight,
length: getDefaultParcelSize().length,
width: getDefaultParcelSize().width,
height: getDefaultParcelSize().height,
Copy link
Contributor

Choose a reason for hiding this comment

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

These 4 lines can be more easily written as

...getDefaultParcelSize(),

@brent-hoover
Copy link
Collaborator

@aldeed I think all four values are/were required because Shippo requires all four to give a shipping quote.

@aldeed
Copy link
Contributor

aldeed commented Jan 17, 2018

@zenweasel But why do they need to be required in the defaults object? Example:

I want to supply only a default weight, so I save the defaults form with only that field populated. It should succeed, resulting in defaults object of { weight: 10 }

Then when I create a product, its weight prop should default to 10 but the other three props can default to 0 like before.

I suppose making all required in the defaults object with default values of 0 is pretty much the same, however it results in more data being stored unnecessarily.

@brent-hoover
Copy link
Collaborator

@aldeed I guess we were thinking that the primary use case would be forcing someone to set these values to be non-zero so we are protecting them from failing during checkout. This is really mostly only relevant in the case of using Shippo though, when using Flat Rate (or other shippers that allow dimensional weight) it doesn't make sense to force anything but weight to be required and if you are using Flat Rate, you don't need any of these values.

@aldeed
Copy link
Contributor

aldeed commented Jan 18, 2018

@zenweasel ah ok. Knowing that, I feel like there is something confusing about how this is implemented. You are entering these values in one place, but they're being used for two different purposes: (1) The defaults when you create a new product/variant, and (2) the parcel values in the cart for shipping purposes.

In reality, I think one is more of a product setting (create each new product with these size values because they are common), and the other is more of a shipping setting (for checkout/processing, use these size values if a product does not have them, because shipping needs them). Should there be two different sets of defaults?

Somewhat related, the docs and UI should be clearer about how inheritance works. If any one value is set on the variant, then it will take all 4 values from the variant rather than the parent, even if some are not set. It's possible the store owner expects per-field inheritance, and I didn't see any documentation about how it works.

@brent-hoover
Copy link
Collaborator

@aldeed Fair. Possibly one version of these settings could be moved into the Shippo settings (where we could force all four values to be set) and another in the "Catalog" section where they could all be optional.

Also, I think that @spencern's original requirement was that there be a "default default" so that even if you didn't set the defaults there would still be some sort of value there. I think this is where we got into all the stuff with the Shop schemas. But maybe these can be set at the shipper level rather than at the shop?

@brent-hoover
Copy link
Collaborator

I've spoke with @spencern about this and although there's been some good work done here I am very tempted to close this PR and rethink the implementation from the ground up again.

@brent-hoover
Copy link
Collaborator

I am closing this PR pending a rethinking of how this should be implemented. This seemed like a simple enough change but was clearly not thought through enough (by me).

@brent-hoover brent-hoover deleted the esther-fix-issue-3019 branch February 13, 2018 21:42
@brent-hoover brent-hoover restored the esther-fix-issue-3019 branch March 28, 2018 03:43
@brent-hoover brent-hoover deleted the esther-fix-issue-3019 branch March 28, 2018 03:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants