2017 © Pedro Peláez
 

library laravel-cashier

Simple package for product management

image

enea/laravel-cashier

Simple package for product management

  • Tuesday, April 17, 2018
  • by eneasdh-fs
  • Repository
  • 1 Watchers
  • 0 Stars
  • 69 Installations
  • PHP
  • 0 Dependents
  • 0 Suggesters
  • 0 Forks
  • 0 Open issues
  • 26 Versions
  • 6 % Grown

The README.md

Deprecated - Laravel Cashier Package

Software License, (*1)

This repository is marked as deprecated and has been replaced by a better and more comprehensive version., (*2)

Reason: We have released Swift Cart, which offers enhanced functionalities, a more user-friendly experience, and improved performance. As a result, this repository will no longer receive updates., (*3)

Deprecation Date: August 4st, 2023., (*4)

This package provides a functionality to manage the sale of products and abstracts all the calculations you need for a sale., (*5)

// create a shopping cart
$document = Invoice::create()->using([Taxes::IVA]);
$shoppingCart = ShoppingManager::initialize($client, $document);

// add a global discount
$discount = Discount::percentage(15)->setCode('PROMOTIONAL');
$shoppingCart->addDiscount($discount);

// add products
$keyboard = $shoppingCart->push(Product::find(1), 5);
$keyboard->addDiscount(Discount::percentage(8)->setCode('ONLY-TODAY'));

$backpack = $shoppingCart->push(Product::find(2));
$backpack->setQuantity(10);

// get totals
$shoppingCart->getSubtotal();
$shoppingCart->getTotalDiscounts();
$shoppingCart->getTotalTaxes();
$shoppingCart->getTotal();

Installation

Laravel Cashier requires PHP 7.4. This version supports Laravel 7, (*6)

To get the latest version, simply require the project using Composer:, (*7)

$ composer require enea/laravel-cashier

And publish the configuration file., (*8)

$ php artisan vendor:publish --provider='Enea\Cashier\CashierServiceProvider'

Class reference

This table defines the implementation of the models necessary for the operation of the package, there are some models that come to help such as: $discount and $document, however it is recommended to replace these models with your own., (*9)

Concrete Abstract Description
$client Enea\Cashier\Contracts\BuyerContract person who makes the purchase
$document Enea\Cashier\Contracts\DocumentContract type of sale document
$product Enea\Cashier\Contracts\ProductContract product being sold
$quote Enea\Cashier\Contracts\QuoteContract quote available for sale
$quotedProduct Enea\Cashier\Contracts\QuotedProductContract quoted product to sell
$discount Enea\Cashier\Modifiers\DiscountContract representation of a discount
$tax Enea\Cashier\Modifiers\TaxContract representation of a tax

Usage

To start a purchase you must use the ShoppingManager::initialize($client, $document)., (*10)

use App\Client;
use Enea\Cashier\Documents\Invoice;
use Enea\Cashier\Facades\ShoppingManager;

$document = Invoice::create()->using([Taxes::IVA]); // or use your own model
$shoppingCart = ShoppingManager::initialize(Client::find(10), $document);

When you initialize a shopping cart, a token is generated so that it can be searched from ShoppingManager::find($token). This function gets the shopping cart from session., (*11)

$token = $shoppingCart->getGeneratedToken();
$shoppingCart = ShoppingManager::find($token); // returns the shopping cart that matches the token

It is also possible that you want to invoice a quote, and to do so you must call the attach function of the $shoppingCart. Doing this creates a QuoteManager instance inside the Shopping Cart, which can be accessed from the $shoppingCart->getQuoteManager() function., (*12)

$shoppingCart->attach($quote);

Now you just have to add products to the shopping cart using $shoppingCart->push($product, $quantity)., (*13)

$keyboard = Product::query()->where('description', 'Keyboard K530-rgb')->firstOrFail();
$productCartItem = $shoppingCart->push($keyboard, 4);

Or you can also pull products from the quote using $shoppingCart->pull($productID)., (*14)

$productCartItem = $shoppingCart->pull($productID);

The push and pull methods returns an instance of ProductCartItem, which provides a lot of useful method., (*15)

// set product quantity
$productCartItem->setQuantity(10);

// configure custom properties
$productCartItem->setProperty(['key' => 'value']);
$productCartItem->putProperty('key', 'value');
$productCartItem->removeProperty('key');

// manage discounts
$productCartItem->addDiscounts($discounts);
$productCartItem->addDiscount($discount);
$productCartItem->getDiscount($discountCode);
$productCartItem->removeDiscount($discountCode);

// get the totals
$cashier = $productCartItem->getCashier();
$cashier->getUnitPrice();
$cashier->getGrossUnitPrice();
$cashier->getNetUnitPrice();
$cashier->getQuantity();
$cashier->getSubtotal();
$cashier->getTotalDiscounts();
$cashier->getTotalTaxes();
$cashier->getTotal();

Example

For this example, we are going to simulate a simple purchase. We need a $client and we will use a $invoice with IVA as a sales document., (*16)

class ShoppingCartController extends Controller
{
  public function start(Client $client): JsonResponse
  {
    $shoppingCart = ShoppingManager::initialize($client);        
    $shoppingCart->setDocument(Invoice::create()->using([Taxes::IGV])); 

    return response()->json([
      'token' => $shoppingCart->getGeneratedToken(),
      'shoppingCart' => $shoppingCart->toArray()
    ]);
  }

  public function addGlobalDiscount(Discount $discount, Request $request): JsonResponse
  {
    $shoppingCart = ShoppingManager::find($request->header('CART-TOKEN'));
    $shoppingCart->addDiscount($discount);

    return response()->json(compact('shoppingCart'));
  }

  public function removeGlobalDiscount(Discount $discount, Request $request): JsonResponse
  {
    $shoppingCart = ShoppingManager::find($request->header('CART-TOKEN'));
    $shoppingCart->removeDiscount($discount->getDiscountCode());

    return response()->json(compact('shoppingCart'));
  }
}

Now we add a controller to manage shopping cart products., (*17)

class ProductManagerController extends Controller
{
  public function addProduct(Product $product, Request $request): JsonResponse
  {
    $shoppingCart = ShoppingManager::find($request->header('CART-TOKEN'));
    $added = $shoppingCart->push($product, $request->get('quantity'));

    return response()->json(compact('shoppingCart', 'added'));
  }

  public function removeProduct(string $productID, Request $request): JsonResponse
  {
    $shoppingCart = ShoppingManager::find($request->header('CART-TOKEN'));
    $shoppingCart->remove($productID);

    return response()->json(compact('shoppingCart'));
  }

  public function updateProductQuantity(string $productID, Request $request): JsonResponse
  {
    $shoppingCart = ShoppingManager::find($request->header('CART-TOKEN'));
    $product = $shoppingCart->find($productID);
    $product->setQuantity($request->get('quantity'));

    return response()->json(compact('shoppingCart', 'product'));
  }

  public function addDiscountToProduct(string $productID, Discount $discount, Request $request): JsonResponse
  {
    $shoppingCart = ShoppingManager::find($request->header('CART-TOKEN'));
    $product = $shoppingCart->find($productID);
    $product->addDiscount($discount);

    return response()->json(compact('shoppingCart'));
  }  

  public function removeDiscountToProduct(string $productID, Discount $discount, Request $request): JsonResponse
  {
    $shoppingCart = ShoppingManager::find($request->header('CART-TOKEN'));
    $product = $shoppingCart->find($productID);
    $product->removeDiscount($discount);

    return response()->json(compact('shoppingCart'));
  }
}

And to finish we save the document in the database., (*18)

class PurchaseController extends Controller
{
  public function store(Request $request): JsonResponse
  {
    $shoppingCart = ShoppingManager::find($request->header('CART-TOKEN'));
    $products = $shoppingCart->collection()->map($this->toOrderProduct());

    $order = DB::Transaction($this->createOrder($shoppingCart, $products));
    $dropped = $this->destroyShoppingCart($shoppingCart->getGeneratedToken());

    return response()->json(compact('order', 'dropped'));
  }

  private function createOrder(ShoppingCart $cart, Collection $products): Closure
  {
    return function() use ($cart, $products): Order {
      $order = Order::create([
        // complete the structure of your model
        'subtotal' => $cart->getSubtotal(),
        'total' => $cart->getTotal(),
        'document_id'd => $cart->getDocument()->getUniqueIdentificationKey(),
      ]);                
      $order->detail()->saveMany($products);    
      return $order;
    };            
  }

  private function toOrderProduct(): Closure
  {
    return fn(ProductCartItem $product) => new OrderProduct([
      'product_id' => $product->getUniqueIdentificationKey(),
      'quantity' => $product->getQuantity(),
      'unit_price' => $product->getCashier()->getUnitPrice(),
      'discount' => $product->getCashier()->getTotalDiscounts(),
      'iva_pct' =>  $product->getTax('IVA')->getPercentage(),
    ]);
  }

  private function destroyShoppingCart(string $token): bool
  {
    ShoppingManager::drop($token);
    return !ShoppingManager::has($token);
  }
}

Cashier

It is responsible for centralizing the calculations to get taxes, discounts and totals for each product. you can find it in Enea\Cashier\Calculations\Cashier., (*19)

Method Description Return
getUnitPrice() unit sales price float
getGrossUnitPrice() gross price (price without tax) float
getNetUnitPrice() net price (price + taxes) float
getSubtotal() subtotal float
getTotalDiscounts() total discounts float
getTotalTaxes() total taxes float
getTotal() final total with discounts and taxes float
getTaxes() all taxes grouped by name Taxed[]
getTax(string $name) tax by name Taxed
getDiscounts() all discounts grouped by code Discounted[]
getDiscount(string $code) discount by code Discounted

Pricing

Cashier separates the prices into 3, getGrossUnitPrice(), getNetUnitPrice() and getUnitPrice(), where the latter is the unit price after evaluating taxes, both included and excluded. $cashier->getUnitPrice() is the function used for all calculations. You can see an example in code from Enea\Tests\Calculations\PriceTest, (*20)

Method getGrossUnitPrice() getNetUnitPrice() getUnitPrice()
Base 100.00 $USD 100.00 $USD 100.00 $USD
Included Taxes IVA(12%), AnotherTax(11%) IVA(12%), AnotherTax(11%) IVA(12%), AnotherTax(11%)
Tax to use IVA(12%) IVA(12%) IVA(12%)
Applied - IVA and AnotherTax IVA
Total 81.30 $USD 100 $USD 90.24 $USD

Configuration

There are a few things you need to know to set up taxes and discounts correctly., (*21)

  • Enea\Cashier\Modifiers\DiscountContract, (*22)

    Represents an applicable discount. There is quite a functional helper implementation in Enea\Cashier\Modifiers\Discount so it is not totally necessary to assign your own model, unless you want full control over the discount codes., (*23)

    namespace Enea\Cashier\Modifiers;
    
    use Enea\Cashier\Calculations\Percentager;
    use Enea\Cashier\Modifiers\DiscountContract;
    
    class Discount implements DiscountContract
    {
      public function getDiscountCode(): string
      {
          return $this->code;
      }
    
      public function getDescription(): string
      {
          return $this->description;
      }
    
      public function extract(float $total): float
      {
          if (! $this->percentage) {
              return $this->discount;
          }
                // logic to calculate a percentage discount
          return Percentager::excluded($total, $this->discount)->calculate();
      }
    }
    
  • Enea\Cashier\Contracts\DocumentContract, (*24)

    Represents the type of document with which the sale will be made and also defines the taxes that will be applied to the products., (*25)

    namespace App\Models;
    
    use Enea\Cashier\Taxes;
    use Enea\Cashier\Contracts\DocumentContract;
    use Illuminate\Database\Eloquent\Model;
    
    class Document extends Model implements DocumentContract
    {
      public function taxesToUse(): array
      {
            // some logic
          return [
            Taxes::IGV, // tax name
          ];
      }
    }
    
  • Enea\Cashier\Modifiers\TaxContract, (*26)

    Represents the tax on the product, the package has a help implementation which can be found in Enea\Cashier\Modifiers\Tax, (*27)

    namespace App\Models;
    
    use Enea\Cashier\Contracts\ProductContract;
    use Enea\Cashier\Modifiers\Tax;
    use Enea\Cashier\Taxes;
    
    class Product extends Model implements ProductContract
    {
      public function getUnitPrice(): float
      {
          return $this->sale_price;
      }
    
      public function getShortDescription(): string
      {
          return $this->short_description;
      }
    
      public function getTaxes(): array
      {
          return [
              Tax::included(Taxes::IGV, $this->igv_pct),
          ];
      }
    }
    

    To use taxes it is necessary to understand that they can be configured in 2 ways, included and excluded, (*28)

Type INCLUDED EXCLUDED
Unit Price 100.00 $USD 100.00 $USD
Tax % 10% 10%
Total Tax 9.09 $USD 10.00 $USD
Net Price 100.00 $USD 110.00 $USD

More documentation

You can find a lot of comments within the source code as well as the tests located in the tests directory., (*29)

The Versions

17/04 2018

dev-master

9999999-dev

Simple package for product management

  Sources   Download

MIT

The Requires

 

The Development Requires

by enea dhack

manager cart shopping sales cashier enea

19/11 2017

dev-development

dev-development

Simple package for product management

  Sources   Download

MIT

The Requires

 

The Development Requires

by enea dhack

manager cart shopping sales cashier enea

19/11 2017

2.0.1

2.0.1.0

Simple package for product management

  Sources   Download

MIT

The Requires

 

The Development Requires

by enea dhack

manager cart shopping sales cashier enea

04/10 2017

2.0.0

2.0.0.0

Simple package for product management

  Sources   Download

MIT

The Requires

 

The Development Requires

by enea dhack

manager cart shopping sales cashier enea

03/10 2017

dev-feature

dev-feature

Simple package for product management

  Sources   Download

MIT

The Requires

 

The Development Requires

by enea dhack

manager cart shopping sales cashier enea

02/10 2017

dev-analysis-8LxjAG

dev-analysis-8LxjAG

Simple package for product management

  Sources   Download

MIT

The Requires

 

The Development Requires

by enea dhack

manager cart shopping sales cashier enea

15/09 2017

1.1.5

1.1.5.0

Simple package for product management

  Sources   Download

MIT

The Requires

 

The Development Requires

by enea dhack

manager cart shopping sales cashier enea

15/09 2017

1.1.4

1.1.4.0

Simple package for product management

  Sources   Download

MIT

The Requires

 

The Development Requires

by enea dhack

manager cart shopping sales cashier enea

15/09 2017

1.1.3

1.1.3.0

Simple package for product management

  Sources   Download

MIT

The Requires

 

The Development Requires

by enea dhack

manager cart shopping sales cashier enea

13/07 2017

1.1.2

1.1.2.0

Simple package for product management

  Sources   Download

MIT

The Requires

 

The Development Requires

by enea dhack

manager cart shopping sales cashier enea

05/07 2017

1.1.1

1.1.1.0

Simple package for product management

  Sources   Download

MIT

The Requires

 

The Development Requires

by enea dhack

manager cart shopping sales cashier enea

02/07 2017

1.1.0

1.1.0.0

Simple package for product management

  Sources   Download

MIT

The Requires

 

The Development Requires

by enea dhack

02/07 2017

dev-downgrade-php

dev-downgrade-php

Simple package for product management

  Sources   Download

MIT

The Requires

 

The Development Requires

by enea dhack

18/06 2017

dev-php7.1-version

dev-php7.1-version

Simple package for product management

  Sources   Download

MIT

The Requires

 

The Development Requires

by enea dhack

18/06 2017

1.0.1

1.0.1.0

Simple package for product management

  Sources   Download

MIT

The Requires

 

The Development Requires

by enea dhack

18/06 2017

1.0.0

1.0.0.0

Simple package for product management

  Sources   Download

MIT

The Requires

 

The Development Requires

by enea dhack

17/06 2017

0.1.4

0.1.4.0

Simple package for product management

  Sources   Download

MIT

The Requires

 

The Development Requires

by enea dhack

16/06 2017

0.1.3

0.1.3.0

Simple package for product management

  Sources   Download

MIT

The Requires

 

The Development Requires

by enea dhack

15/06 2017

0.1.2

0.1.2.0

Simple package for product management

  Sources   Download

MIT

The Requires

 

The Development Requires

by enea dhack

14/06 2017

0.1.1

0.1.1.0

Simple package for product management

  Sources   Download

MIT

The Requires

 

The Development Requires

by enea dhack

13/06 2017

0.1.0

0.1.0.0

Simple package for product management

  Sources   Download

MIT

The Requires

 

The Development Requires

by enea dhack

13/06 2017

0.0.5

0.0.5.0

Simple package for product management

  Sources   Download

MIT

The Requires

 

The Development Requires

by enea dhack

12/06 2017

0.0.4

0.0.4.0

Simple package for product management

  Sources   Download

MIT

The Requires

 

The Development Requires

by enea dhack

12/06 2017

0.0.3

0.0.3.0

Simple package for product management

  Sources   Download

MIT

The Requires

 

The Development Requires

by enea dhack

01/06 2017

0.0.2

0.0.2.0

Simple package for product management

  Sources   Download

MIT

The Requires

 

The Development Requires

by enea dhack

31/05 2017

0.0.1

0.0.1.0

Simple package for product management

  Sources   Download

MIT

The Requires

 

The Development Requires

by enea dhack