src/Entity/Purchase.php line 18

Open in your IDE?
  1. <?php
  2. namespace App\Entity;
  3. use Doctrine\Common\Collections\ArrayCollection;
  4. use Doctrine\Common\Collections\Collection;
  5. use Doctrine\ORM\Mapping as ORM;
  6. use \DateTime as DateTime;
  7. use \DateInterval as DateInterval;
  8. /**
  9.  * @ORM\Entity(repositoryClass="App\Repository\PurchaseRepository")
  10.  * 
  11.  * This class is the equivalent of the user's shopping cart. Users can add multiple purchase items into
  12.  * their Purchase items
  13.  */
  14. class Purchase
  15. {
  16.     
  17.     const STATUS_PROCESSING = -2;   //< status not in use currently 
  18.     const STATUS_ACTIVE = -1;       //< means that the purchase / cart is being edited still.. - TODO change to "current"
  19.     const STATUS_FAILED 0;        //< means that the purchase / cart order failed...
  20.     const STATUS_PROCESSED 1;     //< means that the purchase / cart is done and paid for...
  21.     
  22.     /**
  23.      * @ORM\Id()
  24.      * @ORM\GeneratedValue()
  25.      * @ORM\Column(type="integer")
  26.      */
  27.     private $id;
  28.     
  29.     /**
  30.      * @ORM\Version @ORM\Column(type="integer")
  31.      */
  32.     private $version;
  33.     /**
  34.      * @ORM\OneToMany(targetEntity="App\Entity\PurchaseItem", mappedBy="purchase", cascade={"persist"})
  35.      * @ORM\JoinColumn(nullable=true)
  36.      */
  37.     private $items;
  38.     /**
  39.      * @ORM\Column(type="bigint")
  40.      */
  41.     private $total;
  42.     
  43.     /**
  44.      * @ORM\Column(type="bigint")
  45.      */
  46.     private $discount;
  47.     
  48.     /**
  49.      * @ORM\Column(type="bigint")
  50.      */
  51.     private $tax;
  52.     
  53.     
  54.     /**
  55.      * @ORM\Column(type="string", length=255) - not used
  56.      */
  57.     private $customer;
  58.     /**
  59.      * @ORM\Column(type="string", length=255)
  60.      */
  61.     private $address_line_1;
  62.     /**
  63.      * @ORM\Column(type="string", length=255)
  64.      */
  65.     private $address_line_2;
  66.     /**
  67.      * @ORM\Column(type="string", length=255)
  68.      */
  69.     private $city;
  70.     /**
  71.      * @ORM\Column(type="string", length=255)
  72.      */
  73.     private $state_province;
  74.     /**
  75.      * @ORM\Column(type="string", length=32)
  76.      */
  77.     private $postal_code;
  78.     /**
  79.      * @ORM\Column(type="string", length=255)
  80.      */
  81.     private $country;
  82.     /**
  83.      * @ORM\Column(type="string", length=255)
  84.      */
  85.     private $company;
  86.     /**
  87.      * @ORM\Column(type="string", length=255)
  88.      */
  89.     private $email;
  90.     /**
  91.      * @ORM\Column(type="string", length=32)
  92.      */
  93.     private $phone;
  94.     /**
  95.      * @ORM\Column(type="string", length=4)
  96.      */
  97.     private $last4;
  98.     /**
  99.      * @ORM\Column(type="string", length=128)
  100.      */
  101.     private $transaction_number;
  102.     /**
  103.      * @ORM\Column(type="datetime", nullable=true)
  104.      * 
  105.      * not sure if this is used...
  106.      */
  107.     private $recurring_date;
  108.     /**
  109.      * @ORM\Column(type="datetime", nullable=true)
  110.      */
  111.     private $modified_at;
  112.     /**
  113.      * @ORM\Column(type="datetime", nullable=true)
  114.      */
  115.     private $created_at;
  116.     /**
  117.      * @ORM\OneToMany(targetEntity="App\Entity\PurchaseNote", mappedBy="purchase", cascade={"persist"})
  118.      */
  119.     private $purchaseNotes;
  120.     /**
  121.      * @ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="purchases")
  122.      * @ORM\JoinColumn(nullable=true)
  123.      */
  124.     private $user;
  125.     /**
  126.      * @ORM\Column(type="text", nullable=true)
  127.      */
  128.     private $raw_response;
  129.     /**
  130.      * @ORM\Column(type="string", length=255)
  131.      */
  132.     private $user_agent;
  133.     /**
  134.      * @ORM\Column(type="string", length=64)
  135.      */
  136.     private $user_ip;
  137.     /**
  138.      * @ORM\Column(type="integer")
  139.      */
  140.     private $status;
  141.     /**
  142.      * @ORM\Column(type="string", length=255)
  143.      */
  144.     private $first_name;
  145.     /**
  146.      * @ORM\Column(type="string", length=255)
  147.      */
  148.     private $last_name;
  149.     /**
  150.      * @ORM\Column(type="string", length=16)
  151.      */
  152.     private $expiration_date;
  153.     /**
  154.      * @ORM\Column(type="datetime")
  155.      */
  156.     private $purchased_at;
  157.     /**
  158.      * @ORM\Column(type="bigint")
  159.      */
  160.     private $purchased_amount;
  161.     /**
  162.      * @ORM\Column(type="text")
  163.      */
  164.     private $purchased_data;
  165.     /**
  166.      * @ORM\ManyToMany(targetEntity="App\Entity\Coupon", inversedBy="purchases")
  167.      */
  168.     private $coupons;
  169.     /**
  170.      * @ORM\Column(type="boolean")
  171.      */
  172.     private $terms_agree;
  173.     
  174.     
  175.     /**
  176.      * @ORM\Column(type="string", length=255)
  177.      */
  178.     private $shipping_first_name;
  179.     /**
  180.      * @ORM\Column(type="string", length=255)
  181.      */
  182.     private $shipping_last_name;
  183.     
  184.     /**
  185.      * @ORM\Column(type="string", length=255)
  186.      */
  187.     private $shipping_company;
  188.     
  189.     /**
  190.      * @ORM\Column(type="string", length=255)
  191.      */
  192.     private $shipping_country;
  193.     
  194.     /**
  195.      * @ORM\Column(type="string", length=255)
  196.      */
  197.     private $shipping_address_line_1;
  198.     /**
  199.      * @ORM\Column(type="string", length=255)
  200.      */
  201.     private $shipping_address_line_2;
  202.     /**
  203.      * @ORM\Column(type="string", length=255)
  204.      */
  205.     private $shipping_city;
  206.     /**
  207.      * @ORM\Column(type="string", length=255)
  208.      */
  209.     private $shipping_state_province;
  210.     /**
  211.      * @ORM\Column(type="string", length=32)
  212.      */
  213.     private $shipping_postal_code;
  214.     /**
  215.      * @ORM\Column(type="boolean")
  216.      */
  217.     private $admin_quick_purchase;
  218.     
  219.     
  220.     public function __construct()
  221.     {
  222.         $this->total 0;
  223.         $this->first_name "";
  224.         $this->last_name "";
  225.         $this->customer "";
  226.         $this->address_line_1 "";
  227.         $this->address_line_2 "";
  228.         $this->city "";
  229.         $this->state_province "";
  230.         $this->postal_code "";
  231.         $this->country "";
  232.         $this->company "";
  233.         $this->email "";
  234.         $this->phone "";
  235.         $this->last4 "";
  236.         $this->transaction_number "";
  237.         $this->recurring_date null;
  238.         $this->modified_at = new \DateTime("now");
  239.         $this->created_at = new \DateTime("now");
  240.         $this->raw_response "";
  241.         $this->user_agent "";
  242.         $this->user_ip "";
  243.         $this->status self::STATUS_ACTIVE;
  244.         
  245.         $this->items = new ArrayCollection();
  246.         $this->purchaseNotes = new ArrayCollection();
  247.         
  248.         // is this considered too personal? - won't store for now...
  249.         $this->expiration_date "";
  250.         $this->purchased_at = new \DateTime("now");
  251.         $this->coupons = new ArrayCollection();
  252.         
  253.         $this->terms_agree 0;
  254.         $this->admin_quick_purchase 0;
  255.         $this->purchased_data "";
  256.         $this->purchased_amount 0;
  257.         $this->discount 0;
  258.         $this->tax 0;
  259.         
  260.         
  261.         $this->shipping_first_name "";
  262.         $this->shipping_last_name "";
  263.         $this->shipping_company "";
  264.         $this->shipping_country "";
  265.         $this->shipping_address_line_1 "";
  266.         $this->shipping_address_line_2 "";
  267.         $this->shipping_city "";
  268.         $this->shipping_state_province "";
  269.         $this->shipping_postal_code "";
  270.     }
  271.     public function getId()
  272.     {
  273.         return $this->id;
  274.     }
  275.     
  276.     public function getVersion() {
  277.         return $this->version;
  278.     }
  279.     
  280.     public function getInvoiceNumber ()
  281.     {
  282.         return str_pad($this->id8"0"STR_PAD_LEFT);
  283.     }
  284.     
  285.     /**
  286.      * @return Collection|PurchaseNote[]
  287.      */
  288.     public function  aseNotes(): Collection
  289.     {
  290.         return $this->purchaseNotes;
  291.     }
  292.     public function addPurchaseNote(purchaseNote $purchaseNote): self
  293.     {
  294.         if (!$this->purchaseNotes->contains($purchaseNote)) {
  295.             $this->purchaseNotes[] = $purchaseNote;
  296.             $purchaseNote->setPurchase($this);
  297.         }
  298.         return $this;
  299.     }
  300.     public function removePurchaseNote(purchaseNote $purchaseNote): self
  301.     {
  302.         if ($this->purchaseNotes->contains($purchaseNote)) {
  303.             $this->purchaseNotes->removeElement($purchaseNote);
  304.             // set the owning side to null (unless already changed)
  305.             if ($purchaseNote->getPurchase() === $this) {
  306.                 $purchaseNote->setPurchase(null);
  307.             }
  308.         }
  309.         return $this;
  310.     }
  311.     
  312.     public function getPurchaseNotes()
  313.     {
  314.         return $this->purchaseNotes;
  315.     }
  316.     public function getUser(): ?User
  317.     {
  318.         return $this->user;
  319.     }
  320.     public function setUser(?User $user): self
  321.     {
  322.         $this->user $user;
  323.         return $this;
  324.     }
  325.     
  326.     public function getCurrentItem (): ?PurchaseItem
  327.     {
  328.         for ($i 0$i count($this->items); $i++) {
  329.             $item $this->items[$i];
  330.             if ($item->getStatus() == PurchaseItem::STATUS_CURRENT) {
  331.                 return $item;
  332.             }
  333.         }
  334.         return null;
  335.     }
  336.     
  337.     /**
  338.      * This function will overwrite previous purchase item - not sure if this will cause issues
  339.      * when purchasing between directory and classified listing
  340.      */
  341.     public function setCurrentItem (PurchaseItem $item): self
  342.     {
  343.         $item->setStatus(PurchaseItem::STATUS_CURRENT);
  344.         
  345.         // add the total if available
  346.         $product $item->getProduct();
  347.         if ($product) {
  348.             $this->total += $product->getPrice();
  349.         }
  350.         
  351.         for ($i 0$i count($this->items); $i++) {
  352.             $temp $this->items[$i];
  353.             if ($temp->getStatus() == PurchaseItem::STATUS_CURRENT) {
  354.                 $this->items[$i] = $item;
  355.                 $item->setPurchase($this);
  356.                 return $this;
  357.             }
  358.         }
  359.         
  360.         // if reached simply add to end of array
  361.         $this->items[] = $item;
  362.         return $this;
  363.     }
  364.     /**
  365.      * @return Collection|PurchaseItem[]
  366.      */
  367.     public function getItems(): Collection
  368.     {
  369.         return $this->items;
  370.     }
  371.     
  372.     public function addItem(PurchaseItem $item): self
  373.     {
  374.         if (!$this->items->contains($item)) {
  375.             $this->items[] = $item;
  376.             $item->setPurchase($this);
  377.         }
  378.         return $this;
  379.     }
  380.     
  381.     public function countTotalItems(): int
  382.     {
  383.         $count 0;
  384.         foreach($this->getItems() as $item) {
  385.             $count += $item->getQuantity();
  386.         }
  387.         return $count;
  388.     }
  389.     public function removeItem(PurchaseItem $item): self
  390.     {
  391.         if ($this->items->contains($item)) {
  392.             $this->items->removeElement($item);
  393.             // set the owning side to null (unless already changed)
  394.             if ($item->getPurchase() === $this) {
  395.                 $item->setPurchase(null);
  396.             }
  397.         }
  398.         return $this;
  399.     }
  400.     
  401.     public function getProductTypes() {
  402.         $productTypes = [];
  403.         foreach($this->items as $item) {
  404.             if(!in_array($item->getType(), $productTypes)) {
  405.                 $productTypes[] = $item->getType();
  406.             }
  407.         }
  408.         
  409.         foreach($productTypes as &$type) {
  410.             if($type == "classified") {
  411.                 $type "Classified Listing";
  412.             }
  413.             elseif($type == "directory") {
  414.                 $type "Directory Listing";
  415.             }
  416.             elseif($type == "shop") {
  417.                 $type "RCS Gift Shop";
  418.             }
  419.             elseif($type == "membership") {
  420.                 $type "R-Club Membership";
  421.             }
  422.             else {
  423.                 $type ucwords($type);
  424.             }
  425.         }
  426.         return implode(", "$productTypes);
  427.     }
  428.     
  429.     /**
  430.      * Should be called when a purchase is finalized. - this will serialize all of the product data,
  431.      * and coupon data to keep a historical record of the purchase - this is because entity
  432.      * relations will change and update over time but we want to keep some history the same
  433.      */
  434.     public function finalize ()
  435.     {
  436.         $items = [];
  437.         for ($i 0$i count($this->items); $i++) {
  438.             $item $this->items[$i];
  439.             $items[] = $item->toJSON();
  440.         }
  441.         
  442.         $coupons = [];
  443.         for ($i 0$i count($this->coupons); $i++) {
  444.             $coupon $this->coupons[$i];
  445.             $coupons[] = $coupon->toJSON();
  446.         }
  447.         $breakdownJson $this->calculateBreakdown();
  448.         $breakdownJson substr($breakdownJson["json"], 1, -1); //remove the outer {}'s since we're placing in json below
  449.         
  450.         // store the data
  451.         $this->purchased_data '{"items":['.implode(","$items).'],"coupons":['.implode(","$coupons).'],'.$breakdownJson.'}';
  452.         $this->purchased_amount $this->calculateTotal();
  453.         
  454.     }
  455.     
  456.     //return an array of the purchase breakdown (subtotal, discounts, taxes, total, and all by type)
  457.     public function calculateBreakdown ($items null$coupons null)
  458.     {
  459.         if(is_null($items)) {
  460.             $items $this->items;
  461.         }
  462.         if(is_null($coupons)) {
  463.             $coupons $this->getCoupons();
  464.         }
  465.         
  466.         $subTotalsPerType = [];
  467.         $discountsPerType = [];
  468.         $taxesPerType = [];
  469.         $finalsPerType = [];
  470.         
  471.         for ($i 0$i count($items); $i++) {
  472.             
  473.             $amount $items[$i]->getAmount();
  474.             $product $items[$i]->getProduct();
  475.             if (!$amount) {
  476.                 $amount $product->getPrice();
  477.             }
  478.             
  479.             if(!isset($subTotalsPerType[$product->getType()])) {
  480.                 $subTotalsPerType[$product->getType()] = 0;
  481.                 $discountsPerType[$product->getType()] = 0;
  482.                 $taxesPerType[$product->getType()] = 0;
  483.             }
  484.             $subTotalsPerType[$product->getType()] += $amount;
  485.         }
  486.         
  487.         // apply any assigned coupons... and double check that coupons are valid  
  488.         foreach ($coupons as $coupon) {
  489.             
  490.             //amount discount for this coupon
  491.             $amount 0;
  492.             
  493.             $prod_type $coupon->getProductType();
  494.             
  495.             if(!isset($subTotalsPerType[$prod_type])) {
  496.                 continue;
  497.             }
  498.             
  499.             //$subTotalsPerType[$prod_type];
  500.             
  501.             switch ($coupon->getType()) {
  502.                 
  503.                 case Coupon::TYPE_DISCOUNT:
  504.                     $amount $coupon->getAmount();
  505.                     
  506.                     if (strpos($amount".") === false) {
  507.                         $amount "{$amount}00";
  508.                     }
  509.                     
  510.                     $amount preg_replace("/[^0-9]*/"""$amount);
  511.                     break;
  512.                     
  513.                 case Coupon::TYPE_PERCENTAGE:
  514.                     $amount $coupon->getAmount();
  515.                     /*
  516.                     if (strpos($amount, "%") !== false) {
  517.                         // percentage amount
  518.                         $amount = str_replace("%", "", $amount);
  519.                         $amount = (double) $amount / 100;
  520.                         $amount = ($total * $amount);
  521.                     }
  522.                     
  523.                     else if (strpos($amount, ".") !== false) {
  524.                         // fraction amount
  525.                         $amount = (double) $amount;
  526.                         $amount = ($total * $amount);
  527.                     }*/
  528.                     $amount = (double) $amount 100;
  529.                     $amount = ($subTotalsPerType[$prod_type] * $amount);
  530.                     
  531.                     break;
  532.                     
  533.                 case Coupon::TYPE_FREE_PRODUCT:
  534.                     $couponProducts $coupon->getProducts();
  535.                     // see if we have this product currently attached to the purchase
  536.                     for ($j 0$j count($items); $j++) {
  537.                         $product $items[$j]->getProduct();
  538.                         
  539.                         // if the coupon contains the product add the amount to the discount
  540.                         if ($couponProducts->contains($product)) {
  541.                             $amount $product->getPrice();
  542.                             // set the amount in the coupon... since it was not provided
  543.                             $coupon->setAmount($product->getPrice());
  544.                             break;
  545.                         }
  546.                     }
  547.                     break;
  548.             }
  549.             
  550.             if ($amount) {
  551.                 $discountsPerType[$prod_type] += $amount;
  552.                 $discountsPerType[$prod_type] = min($discountsPerType[$prod_type], $subTotalsPerType[$prod_type]); //can't discount > 100%
  553.             }
  554.             
  555.         }
  556.         
  557.         $subtotal 0;
  558.         $discount 0;
  559.         $tax 0;
  560.         $final 0;
  561.         
  562.         foreach($subTotalsPerType as $prod_type => $total) {
  563.             
  564.             $subtotal += $total;
  565.             
  566.             $finalsPerType[$prod_type] = $subTotalsPerType[$prod_type] - $discountsPerType[$prod_type];
  567.             
  568.             //Calculate taxes. Current only taxing gift shop items, all purchases use California's sales tax rate.
  569.             if($prod_type == Product::PRODUCT_TYPE_SHOP) {
  570.                 $tax_rate 0.0725;
  571.                 $taxesPerType[$prod_type] = $finalsPerType[$prod_type] * $tax_rate;
  572.                 
  573.                 $finalsPerType[$prod_type] += $taxesPerType[$prod_type];
  574.             }
  575.             
  576.             $discount += $discountsPerType[$prod_type];
  577.             $tax += $taxesPerType[$prod_type];
  578.             $final += $finalsPerType[$prod_type];
  579.             
  580.         }
  581.         
  582.         $this->discount $discount;
  583.         $this->tax $tax;
  584.         
  585.         $return = [
  586.             "total" => $final,
  587.             "json" => json_encode([
  588.                 "subTotalsPerType" => $subTotalsPerType,
  589.                 "discountsPerType" => $discountsPerType,
  590.                 "taxesPerType" => $taxesPerType,
  591.                 "finalsPerType" => $finalsPerType,
  592.                 "subtotal" => $subtotal,
  593.                 "discount" => $discount,
  594.                 "tax" => $tax,
  595.                 "total" => $final,
  596.             ])
  597.         ];
  598.         
  599.         return $return;
  600.         //return $final;
  601.         
  602.     }
  603.     
  604.     //Check if this purchase requires shipping info. Currently includes all gift shop items.
  605.     public function requiresShipping(): ?bool {
  606.         $require_shipping false;
  607.         
  608.         foreach($this->items as $item) {
  609.             $product $item->getProduct();
  610.             if($product->getType() == Product::PRODUCT_TYPE_SHOP) {
  611.                 $require_shipping true;
  612.             }
  613.         }
  614.         
  615.         return $require_shipping;
  616.     }
  617.     
  618.     //Check if this purchase will require billing; either now, or in the future if recurring and coupons expire
  619.     public function requiresBilling(): ?bool {
  620.         
  621.         if($this->calculateTotal() > 0) {
  622.             return true;
  623.         }
  624.         
  625.         $recurringItems = [];
  626.         $recurringCoupons = [];
  627.         
  628.         foreach($this->items as $item) {
  629.             $product $item->getProduct();
  630.             if($product->getPaymentRate() && ($product->getPaymentRate() != "none")) {
  631.                 $recurringItems[] = $item;
  632.             }
  633.         }
  634.         
  635.         foreach($this->getCoupons() as $coupon) {
  636.             if($coupon->getRecurringType() == Coupon::RECURRING_UNLIMITED) {
  637.                 $recurringCoupons[] = $coupon;
  638.             }
  639.         }
  640.         
  641.         $breakdown $this->calculateBreakdown($recurringItems$recurringCoupons);
  642.         $total $breakdown["total"];
  643.         return $total 0;
  644.     }
  645.     
  646.     public function calculateTotal (): ?int
  647.     {
  648.         $breakdown $this->calculateBreakdown();
  649.         return $breakdown["total"];
  650.     }
  651.     
  652.     //Return the total without any discounts / taxes
  653.     public function calculateSubTotal (): ?int
  654.     {    
  655.         $subtotal 0;
  656.         
  657.         for ($i 0$i count($this->items); $i++) {
  658.             $amount $this->items[$i]->getAmount();            
  659.             if (!$amount) {
  660.                 $product $this->items[$i]->getProduct();
  661.                 $amount $product->getPrice();
  662.             }
  663.             $subtotal += $amount;
  664.         }
  665.         return $subtotal;
  666.     }
  667.     public function getTotal(): ?int
  668.     {
  669.         $total $this->calculateTotal();
  670.         /*
  671.         $total = 0;
  672.         for ($i = 0; $i < count($this->items); $i++) {
  673.             $amount = $this->items[$i]->getAmount();
  674.             if (!$amount) {
  675.                 $product = $this->items[$i]->getProduct();
  676.                 $amount = $product->getPrice();
  677.             }
  678.             $total += $amount;
  679.         }
  680.         
  681.         // apply any assigned coupons... and make sure coupons are valid
  682.         */
  683.         
  684.         $this->total $total;
  685.         
  686.         return $this->total;
  687.     }
  688.     
  689.     public function getSubTotalUsd ($asDecimal false): string
  690.     {
  691.         $total $this->calculateSubTotal();
  692.         if ($asDecimal) {
  693.             return number_format(($total 100), 2".""");   
  694.         } else {
  695.             return "$" number_format(($total 100), 2);
  696.         }
  697.     }
  698.     
  699.     public function getTotalUsd ($asDecimal false): string
  700.     {
  701.         $total $this->getTotal();
  702.         if ($asDecimal) {
  703.             return number_format(($total 100), 2".""");   
  704.         } else {
  705.             return "$" number_format(($total 100), 2);
  706.         }
  707.     }
  708.     public function setTotal(int $total): self
  709.     {
  710.         $this->total $total;
  711.         return $this;
  712.     }
  713.     
  714.     //Total discount
  715.     public function getDiscount(): ?int
  716.     {
  717.         return $this->discount;
  718.     }
  719.     
  720.     public function getDiscountUsd ($asDecimal false): string
  721.     {
  722.         $discount $this->getDiscount();
  723.         if ($asDecimal) {
  724.             return number_format(($discount 100), 2".""");   
  725.         } else {
  726.             return "$" number_format(($discount 100), 2);
  727.         }
  728.     }
  729.     public function setDiscount(int $discount): self
  730.     {
  731.         $this->discount $discount;
  732.         return $this;
  733.     }
  734.     
  735.     //Total taxes
  736.     public function getTax(): ?int
  737.     {
  738.         return $this->tax;
  739.     }
  740.     
  741.     public function getTaxUsd ($asDecimal false): string
  742.     {
  743.         $tax $this->getTax();
  744.         if ($asDecimal) {
  745.             return number_format(($tax 100), 2".""");   
  746.         } else {
  747.             return "$" number_format(($tax 100), 2);
  748.         }
  749.     }
  750.     public function setTax(int $tax): self
  751.     {
  752.         $this->tax $tax;
  753.         
  754.         return $this;
  755.     }
  756.     
  757.     public function getRecurringDate(): ?\DateTimeInterface
  758.     {
  759.         return $this->recurring_date;
  760.     }
  761.     public function setRecurringDate(\DateTimeInterface $recurring_date): self
  762.     {
  763.         $this->recurring_date $recurring_date;
  764.         return $this;
  765.     }
  766.     
  767.     public function getModifiedAt(): ?\DateTimeInterface
  768.     {
  769.         return $this->modified_at;
  770.     }
  771.     public function setModifiedAt(\DateTimeInterface $modified_at): self
  772.     {
  773.         $this->modified_at $modified_at;
  774.         return $this;
  775.     }
  776.     
  777.     public function getCreatedAt(): ?\DateTimeInterface
  778.     {
  779.         return $this->created_at;
  780.     }
  781.     public function setCreatedAt(\DateTimeInterface $created_at): self
  782.     {
  783.         $this->created_at $created_at;
  784.         return $this;
  785.     }
  786.     public function getStatus(): ?int
  787.     {
  788.         return $this->status;
  789.     }
  790.     
  791.     public function getStatusText (): string
  792.     {
  793.         switch ($this->status) {
  794.             case self::STATUS_PROCESSING:
  795.                 return "Processing";
  796.             case self::STATUS_ACTIVE:
  797.                 return "In Cart";
  798.             case self::STATUS_FAILED:
  799.                 return "Failed";
  800.             case self::STATUS_PROCESSED:
  801.             default:
  802.                 return "Processed";
  803.         }
  804.     }
  805.     public function setStatus(int $status): self
  806.     {
  807.         //return if the status didn't change
  808.         if($this->status == $status) {
  809.             return $this;
  810.         }
  811.         
  812.         $this->status $status;
  813.         
  814.         // if the purchase is processed - move through all purchase items and content and set appropriate 
  815.         // renewal and expiration dates.
  816.         
  817.         if ($status == self::STATUS_PROCESSED) {
  818.             
  819.             $this->purchased_at = new \DateTime("now");
  820.             
  821.             // this should really be cleaned up to follow a better flow...
  822.             $this->finalize();
  823.             
  824.             for ($i 0$i count($this->items); $i++) {
  825.                 $item $this->items[$i];
  826.                 $product $item->getProduct();
  827.                 $rate strtoupper($product->getPaymentRate());
  828.                 $content $item->getContent();
  829.                 
  830.                 // grab the purchased date
  831.                 $purchased $this->getPurchasedAt();
  832.                 
  833.                 // 1 month interval default
  834.                 $interval = new DateInterval("P1M");
  835.                 $intervalSet false;
  836.                 foreach($item->getProductOptionValues() as $productOptionValue) {
  837.                     $productOption $productOptionValue->getProductOption();
  838.                     if($productOption && $productOption->getTitle() == "duration") {
  839.                         $interval = new DateInterval("P".$productOptionValue->getValue()."D");
  840.                         $intervalSet true;
  841.                     }
  842.                 }
  843.                 if(!$intervalSet) {
  844.                     switch ($rate) {
  845.                         case Product::RENEW_MONTHLY:
  846.                             $interval = new DateInterval("P1M");
  847.                             break;
  848.                             
  849.                         case Product::RENEW_YEARLY:
  850.                             $interval = new DateInterval("P1Y");
  851.                             break;
  852.                             
  853.                         case Product::RENEW_NOT_SET:
  854.                         default: 
  855.                             // ugly hack ... try to determine from title of product...
  856.                             $title $product->getTitle();
  857.                                                     
  858.                             if (stripos($title"annual") !== false) {
  859.                                 $interval = new DateInterval("P1Y");
  860.                             }
  861.                             
  862.                             else if (stripos($title"30 day") !== false) {
  863.                                 $interval = new DateInterval("P30D");
  864.                             }
  865.                             
  866.                             else if (stripos($title"60 day") !== false) {
  867.                                 $interval = new DateInterval("P60D");
  868.                             }
  869.                             
  870.                             else if (stripos($title"90 day") !== false) {
  871.                                 $interval = new DateInterval("P90D");
  872.                             }
  873.                             
  874.                             else {
  875.                                 // catch all - give the content 1 active month if can't figure out renewal time
  876.                                 $interval = new DateInterval("P1M");
  877.                             }
  878.                             break;
  879.                     }
  880.                     
  881.                 }
  882.                 
  883.                 // set the proper expiration times ... 
  884.                 if ($content) {
  885.                     // update the published at 
  886.                     $content->setPublishedAt(new DateTime("now"));
  887.                 
  888.                     $expires $content->getExpiresAt();
  889.                     
  890.                     // if exists and is valid
  891.                     if ($expires && new DateTime("now") < $expires) {
  892.                         // content has not yet expired
  893.                         $expires = clone $expires;
  894.                         $content->setExpiresAt($expires->add($interval));
  895.                         $content->setStatus(Content::STATUS_ACTIVE);
  896.                     }
  897.                     
  898.                     else {
  899.                         // content has either expired or is invalid
  900.                         $expires = clone $purchased;
  901.                         $content->setExpiresAt($expires->add($interval));
  902.                         $content->setStatus(Content::STATUS_ACTIVE);
  903.                     }
  904.                 }
  905.                 
  906.                 //shop items don't expire
  907.                 if($product->getType() == Product::PRODUCT_TYPE_SHOP) {
  908.                     $item->setStatus(PurchaseItem::STATUS_ACTIVE);
  909.                 }
  910.                 else {
  911.                     // the below really shouldn't matter because we are making a new purchase item when renewing
  912.                     $expires $item->getExpiresAt();
  913.                     if ($expires && new DateTime("now") < $expires) {
  914.                         $expires = clone $expires;
  915.                         $item->setExpiresAt($expires->add($interval));
  916.                         $item->setStatus(PurchaseItem::STATUS_ACTIVE);
  917.                     }
  918.                     
  919.                     else {
  920.                         // item
  921.                         $expires = clone $purchased;
  922.                         $item->setExpiresAt($expires->add($interval)); 
  923.                         $item->setStatus(PurchaseItem::STATUS_ACTIVE);                   
  924.                     }
  925.                 }
  926.                 
  927.                 $item->setMonthsRenewed(1);
  928.                 
  929.                 //Handle the RCS Club Membership product
  930.                 if($product->getType() == Product::PRODUCT_TYPE_MEMBERSHIP) {
  931.                     $user $this->getUser();
  932.                     if($user) {
  933.                         $user->setMember(1);
  934.                         
  935.                         //handle company memberships. should make this a different type, but for now just checking the title
  936.                         if(strpos($product->getTitle(), 'Company') !== false) {
  937.                             $user->setMember(2);
  938.                         }
  939.                         elseif(strpos($product->getTitle(), 'Contractor') !== false) {
  940.                             $user->setMember(3);
  941.                         }
  942.                         
  943.                         //Changed to opt-in
  944.                         //if(strlen($user->getUserMetaValueByKey("member_notification_forum")) == 0) {$user->setUsermetum("member_notification_forum", "1");};
  945.                         //if(strlen($user->getUserMetaValueByKey("member_notification_page")) == 0) {$user->setUsermetum("member_notification_page", "1");};
  946.                         //if(strlen($user->getUserMetaValueByKey("member_notification_classifieds")) == 0) {$user->setUsermetum("member_notification_classifieds", "1");};
  947.                         
  948.                         //set to auto renew. should move this above when classified / directory auto renewal is working
  949.                         $item->setStatus(PurchaseItem::STATUS_ACTIVE_RENEWING);
  950.                     }
  951.                 }
  952.                 
  953.                 
  954.             }
  955.             
  956.             // all dates should be updated
  957.             
  958.             
  959.         }
  960.         return $this;
  961.     }
  962.     
  963.     public function getCustomer(): ?string
  964.     {
  965.         return $this->customer;
  966.     }
  967.     public function setCustomer(string $customer null): self
  968.     {
  969.         $this->customer $customer $customer "";
  970.         return $this;
  971.     }
  972.     public function getAddressLine1(): ?string
  973.     {
  974.         return $this->address_line_1;
  975.     }
  976.     public function setAddressLine1(string $address_line_1 null): self
  977.     {
  978.         $this->address_line_1 $address_line_1 $address_line_1 "";
  979.         return $this;
  980.     }
  981.     public function getAddressLine2(): ?string
  982.     {
  983.         return $this->address_line_2;
  984.     }
  985.     public function setAddressLine2(string $address_line_2 null): self
  986.     {
  987.         $this->address_line_2 $address_line_2 $address_line_2 "";
  988.         return $this;
  989.     }
  990.     public function getCity(): ?string
  991.     {
  992.         return $this->city;
  993.     }
  994.     public function setCity(string $city null): self
  995.     {
  996.         $this->city $city $city "";
  997.         return $this;
  998.     }
  999.     public function getStateProvince(): ?string
  1000.     {
  1001.         return $this->state_province;
  1002.     }
  1003.     public function setStateProvince(string $state_province null): self
  1004.     {
  1005.         $this->state_province $state_province $state_province "";
  1006.         return $this;
  1007.     }
  1008.     public function getPostalCode(): ?string
  1009.     {
  1010.         return $this->postal_code;
  1011.     }
  1012.     public function setPostalCode(string $postal_code null): self
  1013.     {
  1014.         $this->postal_code $postal_code $postal_code "";
  1015.         return $this;
  1016.     }
  1017.     public function getCountry(): ?string
  1018.     {
  1019.         return $this->country;
  1020.     }
  1021.     public function setCountry(string $country null): self
  1022.     {
  1023.         $this->country $country $country "";
  1024.         return $this;
  1025.     }
  1026.     public function getCompany(): ?string
  1027.     {
  1028.         return $this->company;
  1029.     }
  1030.     public function setCompany(string $company null): self
  1031.     {
  1032.         $this->company $company $company "";
  1033.         return $this;
  1034.     }
  1035.     public function getEmail(): ?string
  1036.     {
  1037.         return $this->email;
  1038.     }
  1039.     public function setEmail(string $email null): self
  1040.     {
  1041.         $this->email $email $email "";
  1042.         return $this;
  1043.     }
  1044.     public function getPhone(): ?string
  1045.     {
  1046.         return $this->phone;
  1047.     }
  1048.     public function setPhone(string $phone null): self
  1049.     {
  1050.         $this->phone $phone $phone "";
  1051.         return $this;
  1052.     }
  1053.     public function getLast4(): ?string
  1054.     {
  1055.         return $this->last4;
  1056.     }
  1057.     public function setLast4(string $last4 null): self
  1058.     {
  1059.         $this->last4 $last4 $last4 "";
  1060.         return $this;
  1061.     }
  1062.     public function getTransactionNumber()
  1063.     {
  1064.         return $this->transaction_number;
  1065.     }
  1066.     public function setTransactionNumber(string $transaction_number null): self
  1067.     {
  1068.         $this->transaction_number $transaction_number $transaction_number "";
  1069.         return $this;
  1070.     }
  1071.     
  1072.     public function __toString ()
  1073.     {
  1074.         return "Purchase: {$this->id}";
  1075.     }
  1076.     public function getFirstName(): ?string
  1077.     {
  1078.         return $this->first_name;
  1079.     }
  1080.     public function setFirstName(string $first_name null): self
  1081.     {
  1082.         $this->first_name $first_name $first_name "";
  1083.         return $this;
  1084.     }
  1085.     public function getLastName(): ?string
  1086.     {
  1087.         return $this->last_name;
  1088.     }
  1089.     public function setLastName(string $last_name null): self
  1090.     {
  1091.         $this->last_name $last_name $last_name "";
  1092.         return $this;
  1093.     }
  1094.     public function getExpirationDate(): ?string
  1095.     {
  1096.         return $this->expiration_date;
  1097.     }
  1098.     public function setExpirationDate(string $expiration_date): self
  1099.     {
  1100.         $this->expiration_date $expiration_date;
  1101.         return $this;
  1102.     }
  1103.     
  1104.     public function getRawResponse(): ?string
  1105.     {
  1106.         return $this->raw_response;
  1107.     }
  1108.     public function setRawResponse(?string $raw_response): self
  1109.     {
  1110.         $this->raw_response $raw_response;
  1111.         return $this;
  1112.     }
  1113.     public function getUserAgent(): ?string
  1114.     {
  1115.         return $this->user_agent;
  1116.     }
  1117.     public function setUserAgent(string $user_agent): self
  1118.     {
  1119.         $this->user_agent $user_agent;
  1120.         return $this;
  1121.     }
  1122.     public function getUserIp(): ?string
  1123.     {
  1124.         return $this->user_ip;
  1125.     }
  1126.     public function setUserIp(string $user_ip): self
  1127.     {
  1128.         $this->user_ip $user_ip;
  1129.         return $this;
  1130.     }
  1131.     public function getPurchasedAt(): ?\DateTimeInterface
  1132.     {
  1133.         return $this->purchased_at;
  1134.     }
  1135.     public function setPurchasedAt(\DateTimeInterface $purchased_at): self
  1136.     {
  1137.         $this->purchased_at $purchased_at;
  1138.         return $this;
  1139.     }
  1140.     public function getPurchasedAmount(): ?int
  1141.     {
  1142.         return $this->purchased_amount;
  1143.     }
  1144.     public function setPurchasedAmount(int $purchased_amount): self
  1145.     {
  1146.         $this->purchased_amount $purchased_amount;
  1147.         return $this;
  1148.     }
  1149.     public function getPurchasedData(): ?string
  1150.     {
  1151.         return $this->purchased_data;
  1152.     }
  1153.     public function setPurchasedData(string $purchased_data): self
  1154.     {
  1155.         $this->purchased_data $purchased_data;
  1156.         return $this;
  1157.     }
  1158.     /**
  1159.      * @return Collection|Coupon[]
  1160.      */
  1161.     public function getCoupons(): Collection
  1162.     {
  1163.         return $this->coupons;
  1164.     }
  1165.     public function addCoupon(Coupon $coupon): self
  1166.     {
  1167.         if (!$this->coupons->contains($coupon)) {
  1168.             $this->coupons[] = $coupon;
  1169.         }
  1170.         return $this;
  1171.     }
  1172.     public function removeCoupon(Coupon $coupon): self
  1173.     {
  1174.         if ($this->coupons->contains($coupon)) {
  1175.             $this->coupons->removeElement($coupon);
  1176.         }
  1177.         return $this;
  1178.     }
  1179.     
  1180.     public function removeCoupons (): self
  1181.     {
  1182.         foreach ($this->coupons as $coupon) {
  1183.             $this->removeCoupon($coupon);
  1184.         }
  1185.         return $this;
  1186.     }
  1187.     public function getTermsAgree(): ?bool
  1188.     {
  1189.         return $this->terms_agree;
  1190.     }
  1191.     public function setTermsAgree(bool $terms_agree): self
  1192.     {
  1193.         $this->terms_agree $terms_agree;
  1194.         return $this;
  1195.     }
  1196.     public function getAdminQuickPurchase(): ?bool
  1197.     {
  1198.         return $this->admin_quick_purchase;
  1199.     }
  1200.     public function setAdminQuickPurchase(bool $admin_quick_purchase): self
  1201.     {
  1202.         $this->admin_quick_purchase $admin_quick_purchase;
  1203.         return $this;
  1204.     }
  1205.     
  1206.     public function getBilling(): ?string
  1207.     {
  1208.         return $this->getFirstName() . " " $this->getLastName() . ($this->getFirstName() ? ", " "") .
  1209.         $this->getCompany() . ($this->getCompany() ? ", " "") . $this->getAddressLine1() . " " .
  1210.         $this->getAddressLine2() . ($this->getAddressLine1() ? ", " "") . $this->getCity() . ($this->getCity() ? ", " "") .
  1211.         $this->getStateProvince() . ($this->getStateProvince() ? ", " "") . $this->getPostalCode()
  1212.         ;
  1213.     }
  1214.     
  1215.     
  1216.     
  1217.         
  1218.     public function getShippingFirstName(): ?string
  1219.     {
  1220.         return $this->shipping_first_name;
  1221.     }
  1222.     public function setShippingFirstName(string $shipping_first_name null): self
  1223.     {
  1224.         $this->shipping_first_name $shipping_first_name $shipping_first_name "";
  1225.         return $this;
  1226.     }
  1227.     public function getShippingLastName(): ?string
  1228.     {
  1229.         return $this->shipping_last_name;
  1230.     }
  1231.     public function setShippingLastName(string $shipping_last_name null): self
  1232.     {
  1233.         $this->shipping_last_name $shipping_last_name $shipping_last_name "";
  1234.         return $this;
  1235.     }
  1236.     
  1237.     public function getShippingCompany(): ?string
  1238.     {
  1239.         return $this->shipping_company;
  1240.     }
  1241.     public function setShippingCompany(string $shipping_company null): self
  1242.     {
  1243.         $this->shipping_company $shipping_company $shipping_company "";
  1244.         return $this;
  1245.     }
  1246.     
  1247.     public function getShippingCountry(): ?string
  1248.     {
  1249.         return $this->shipping_country;
  1250.     }
  1251.     public function setShippingCountry(string $shipping_country null): self
  1252.     {
  1253.         $this->shipping_country $shipping_country $shipping_country "";
  1254.         return $this;
  1255.     }
  1256.     
  1257.     public function getShippingAddressLine1(): ?string
  1258.     {
  1259.         return $this->shipping_address_line_1;
  1260.     }
  1261.     public function setShippingAddressLine1(string $shipping_address_line_1 null): self
  1262.     {
  1263.         $this->shipping_address_line_1 $shipping_address_line_1 $shipping_address_line_1 "";
  1264.         return $this;
  1265.     }
  1266.     public function getShippingAddressLine2(): ?string
  1267.     {
  1268.         return $this->shipping_address_line_2;
  1269.     }
  1270.     public function setShippingAddressLine2(string $shipping_address_line_2 null): self
  1271.     {
  1272.         $this->shipping_address_line_2 $shipping_address_line_2 $shipping_address_line_2 "";
  1273.         return $this;
  1274.     }
  1275.     public function getShippingCity(): ?string
  1276.     {
  1277.         return $this->shipping_city;
  1278.     }
  1279.     public function setShippingCity(string $shipping_city null): self
  1280.     {
  1281.         $this->shipping_city $shipping_city $shipping_city "";
  1282.         return $this;
  1283.     }
  1284.     public function getShippingStateProvince(): ?string
  1285.     {
  1286.         return $this->shipping_state_province;
  1287.     }
  1288.     public function setShippingStateProvince(string $shipping_state_province null): self
  1289.     {
  1290.         $this->shipping_state_province $shipping_state_province $shipping_state_province "";
  1291.         return $this;
  1292.     }
  1293.     public function getShippingPostalCode(): ?string
  1294.     {
  1295.         return $this->shipping_postal_code;
  1296.     }
  1297.     public function setShippingPostalCode(string $shipping_postal_code null): self
  1298.     {
  1299.         $this->shipping_postal_code $shipping_postal_code $shipping_postal_code "";
  1300.         return $this;
  1301.     }
  1302.     
  1303. }