npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@restorecommerce/cart

v1.0.9

Published

<img src="http://img.shields.io/npm/v/@restorecommerce/cart.svg?style=flat-square" alt="">[![Build Status][build]](https://travis-ci.org/restorecommerce/cart?branch=master)[![Dependencies][depend]](https://david-dm.org/restorecommerce/cart)[![Coverage Sta

Downloads

75

Readme

@restorecommerce/cart

Build StatusDependenciesCoverage Status

An backend agnostic purely data-driven shopping cart that can be used on client- and server side.

Features

  • Item handling (add, remove etc.)
  • VAT calculation with extensible tax model
    • EU tax calculation built-in (for sub-threshold based taxation)
  • Shipping cost calculation based on selected courier and plan
  • Extensibility for adding couriers
  • Totals calculation based on items, taxes and shipping
  • Can run completely offline
  • Local storage on browsers via pluggable serializers
  • Back-end agnostic
  • Fully typed

Usage

Basic Example

Cart Instantiation:

const cart = new Cart({
  serializer: new MockSerializer(),
  shippingMethod: new Courier({
    source: JSON.stringify(data.publicDHL),
    shipping: {originCountry: 'DE'}
  }),
  taxOriginCountry: 'DE',
  taxes: {
    vat_standard: {
      rate: new Decimal(1.19),
      desc: '+ VAT 19%'
    },
    vat_reduced: {
      rate: new Decimal(1.07),
      desc: '+ VAT 7%'
    }
  }
});

Setting serializer and shippingMethod is optional.

setDestinationCountry(country: string)

cart.setDestinationCountry('LV');

getShippingMethod()

cart.getShippingMethod();

Output:

Courier {
  _source: {
    assumptions: {
      currency: 'eur',
      dimensions: [Object],
      length: 'mm',
      ranges: [Object],
      weight: 'gram'
    },
    zones: {
      '1': [Object],
      '2': [Object],
      '3': [Object],
      '4': [Object],
      '5': [Object],
      '6': [Object],
      '7': [Object],
      '8': [Object],
      national: [Object]
    }
  },
  _shipping: {
    destinationCountry: 'LV',
    originCountry: 'DE' }
}

getShipping()

There is no setShipping setter, since info about shipping is taken from ShippingMethod's source property.

cart.getShipping(); // calculates shipping cost

Output:

{
  price: '15.99',
  taxType: 'vat_standard',
  maxWeight: 5000,
  type: 'package',
  zone: '1',
  human: {
    zone: '1 (all EU countries)',
    offer: 'Package up to 5kg',
  }
}

getSerializer()

cart.getSerializer();

getTaxRates()

cart.getTaxRates();

Output:

{
  taxes: {
    vat_standard: {
      rate: '1.19',
      desc: '+ VAT 19%'
    },
    vat_reduced: {
      rate: '1.07',
      desc: '+ VAT 7%'
    }
  }
}

addItems(items: IItem[])

cart.addItems([{
  sku: 'cr2-blue',
  price: new Decimal('12.95'), // Price
  taxType: 'vat_reduced',
  weight: 210, // grams
  height: 2.20, // cm
  width: 13.5, // cm
  depth: 8.22, // cm
  quantity: 7,
}, {
  sku: 'cr5-red',
  price: new Decimal('1.10'),
  taxType: 'vat_standard',
  weight: 210, // grams
  height: 2.20, // cm
  width: 13.5, // cm
  depth: 8.22, // cm
  quantity: 15,
}, {
  sku: 'cr3-yellow',
  price: new Decimal('2.48'),
  taxType: 'vat_standard',
  weight: 210, // grams
  height: 2.20, // cm
  width: 13.5, // cm
  depth: 8.22, // cm
  quantity: 3,
}]);

remItem(sku: string)

cart.remItem('cr3-yellow');

getItems()

cart.getItems();

Output:

[
  {
    sku: 'cr2-blue',
    price: 12.95,
    taxType: 'vat_reduced',
    weight: 210,
    height: 2.2,
    width: 13.5,
    depth: 8.22,
    quantity: 7
  },
  {
    sku: 'cr5-red',
    price: 1.10,
    taxType: 'vat_standard',
    weight: 210,
    height: 2.2,
    width: 13.5,
    depth: 8.22,
    quantity: 15
   }
]

setCustomer(customer: ICustomer)

cart.setCustomer({
  type: CustomerType.COMMERCIAL,
});

setCustomerType(type: CustomerType)

This modifies the customer..

cart.setCustomerType(CustomerType.PRIVATE);

getCustomer()

cart.getCustomer();

Output:

{
  type: 1 // 0 - COMMERCIAL / 1 - PRIVATE
}

modifyItem(item: any)

cart.modifyItem({
  sku: 'cr5-red',
  // no update for other Cart properties as no change
  quantity: 10, // change only quantity
});

modifyItemQuantity(sku: string, quantity: number)

cart.modifyItemQuantity('cr5-red', 5); // adds 5 to initial quantity

getItemQuantity(sku: string)

`Amount of cr2-blue = ${cart.getItemQuantity('cr5-red')}`;

Output:

Amount of cr5-red = 15 // 10 + 5

getItemCount()

`Amount of unique products = ${cart.getItemCount()}`;

Output:

Amount of unique products = 2

getGrandQuantity()

`Amount of all products = ${cart.getGrandQuantity()}`;

Output:

Amount of all products = 22 // 15 + 7

getTaxes(keepOriginalTaxType?: boolean )

cart.getTaxes();

Output:

netPrice and rate are instances of Decimal.

{
  vat_standard: {
    netPrice: '32.49',
    rate: '1.19',
    desc: '+ VAT 19%'
  },
  vat_reduced: {
    netPrice: '90.65',
    rate: '1.07',
    desc: '+ VAT 7%'
  }
}

static round(money: Money)

Parameter could be number|string|Decimal. Returns string.

Cart.round(1.120); // '1.12'
Cart.round('1.123'); // '1.13'
Cart.round(new Decimal(1.125)); // '1.13'
Cart.round(new Decimal('1.127')); // '1.13'

getTotalNet()

Total net (without taxes).

Cart.round(cart.getTotalNet()); // '123.14'

Calculation:

1) customer.billing.countryCode === 'LV' => VAT + 19% / 7%
2) cr2_blue: Price * quantity => 12.95 * 7  = 90.65
3) cr5_red:  Price * quantity => 1.10 * 15  = 16.5
4) Max Weight of all items = 22 items * 210 grams  = 4620 grams =>
shipping = 15.99 euro for package less than 5000 grams to EU
5) sum = 90.65 + 16.5 + 15.99 = 123.14

getTotalGross()

Total gross (with taxes).

Cart.round(cart.getTotalGross()); // '135.66'

Calculation:

1) cr2_blue => cr2_blue + VAT 7% = 90.65 * 1.07 = 96.9955
2) cr5_red => cr5_red + VAT 19% = 16.5 * 1.19 = 19.635
3) shipping => shipping + VAT 19% = 15.99 * 1.19 = 19.0281
4) sum  =>  96.9955 + 19.635 + 19.0281 = 135.6586
5) round(135.6586) = 135.66

Tests

More examples of using the Cart can be found in test/index.ts.

Reference

|Method | Description | |---|---| |getItems(): IItems / undefined | Get Items | |private setItems(items: IItem[]): void| Set items | |getCustomer(): ICustomer / undefined| Get customer | |setCustomer(customer: ICustomer): void| Set customer | |getShippingMethod(): IShippingMethod/ undefined| Get shipping method | |setShippingMethod(shippingMethod: IShippingMethod): void| Set shipping method | |getSerializer(): ISerializer / undefined| Get serializer | |setSerializer(serializer: ISerializer): void| Set serializer | |getTaxRates(): TaxRates| Get tax rates | |private setTaxRates(taxRates: TaxRates)| Set tax rates | |setCustomerType(type: CustomerType): void | Set customer's type (PRIVATE/COMMERCIAL) | |setDestinationCountry(country: string): void| Set destination country | |addItems(items: IItem[]): void | Add item/items to the cart | |remItem(sku: string): void| Remove item from the cart by SKU (Stock Keeping Unit) | |modifyItem(item: any)| Update item | |modifyItemQuantity(sku: string, quantity: number): void | Modify item's quantity | |getItemCount(): number | Get item count | |getItemQuantity(sku: string): number | Get quantity of particular item | |getGrandQuantity(): number | Item count x quantity of each item | |getTaxes(keepOriginalTaxType?: boolean ): { [taxType: string]: { netPrice: Decimal, rate: Decimal, desc: string, price: Decimal } } | Get tax list, with ratios and additive costs | |getTotalNet(): number | Get sum of item and shipping costs (taxes excluded) | |getTotalGross(): number| Get sum of item and shipping costs (taxes included) | |getShipping(): { price: Money, [prop: string]: any } | Get item's shipping info | |static round(money: Money): string | Convert money type (number/string/Decimal) to string with rounding |

Development

To lint, transpile and test, run:

npm test

Couriers

IShippingMethod interface implementations:

Courier Plans:

Calculation Logic

Taxes

For VAT calculation, the cart applies the EU ruleset for taxation which is exemplified in the following with a shop that is located in Germany and customer location location/ type being:

  • Germany private: VAT applies
  • Germany commercial: VAT applies
  • Other EU countries private: VAT applies
  • Other EU countries commercial: VAT free
  • Non-EU countries private: VAT free
  • Non-EU countries commercial: VAT free

This also works for other countries as it's just a simplification of this for most countries. Sales taxes and other country specific taxes can be added.

Legal background: