Skip to content

Commit

Permalink
Add custom DynamicTaxModeProduct
Browse files Browse the repository at this point in the history
  • Loading branch information
muzzwood committed Mar 2, 2024
1 parent 931e6ec commit 4c30e3b
Show file tree
Hide file tree
Showing 8 changed files with 293 additions and 4 deletions.
3 changes: 2 additions & 1 deletion core/components/commerce_dynamictaxmode/composer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"require": {
"php": ">=7.4.0"
"php": ">=7.4.0",
"ext-json": "*"
},
"autoload": {
"psr-4": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
<?php

$_lang['commerce.DynamicTaxModeProduct'] = 'Dynamic Tax Mode Product';
$_lang['commerce.add_DynamicTaxModeProduct'] = 'Add Dynamic Tax Mode Product';

$_lang['commerce_dynamictaxmode'] = 'Dynamic Tax Mode';
$_lang['commerce_dynamictaxmode.description'] = 'Dynamically switches between inclusive and exclusive tax modes on the order level';
$_lang['commerce_dynamictaxmode.business_price'] = 'Business Price';
$_lang['commerce_dynamictaxmode.session_key'] = 'Session Key';
$_lang['commerce_dynamictaxmode.session_key.description'] = 'Set the name of the session key that this module will check to determine the tax mode for an order.';
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
<?php

use modmore\Commerce\Admin\Widgets\Form\PricingField;
use modmore\Commerce\Pricing\Exceptions\InvalidPriceTypeException;
use modmore\Commerce\Pricing\Interfaces\ItemPricingInterface;
use modmore\Commerce\Pricing\ProductPricing;

class DynamicTaxModeProduct extends comProduct
{
protected ?comModule $module = null;
protected string $sessionKey = '';

public function __construct(xPDO &$xpdo)
{
parent::__construct($xpdo);

// Get session key
if (empty($this->sessionKey)) {
$this->sessionKey = \modmore\Commerce_DynamicTaxMode\Module::DEFAULT_SESSION_KEY;
$this->module = $this->adapter->getObject(comModule::class, [
'class_name' => 'modmore\Commerce_DynamicTaxMode\Module',
]);
if ($this->module && !empty($this->module->getProperty('session_key'))) {
$this->sessionKey = $this->module->getProperty('session_key');
}
}
}

/**
* @param comCurrency $currency
* @return ItemPricingInterface
*/
public function getBusinessPricing(comCurrency $currency): ?ItemPricingInterface
{
$instance = $this->getBusinessPricingInstance($currency);
if (!$instance instanceof ItemPricingInterface) {
$legacyPrice = $this->getPrice();
$newPrice = new \modmore\Commerce\Pricing\Price($currency, $legacyPrice->getPriceForCurrency($currency));
$instance = new ProductPricing($currency, $newPrice);
}

return $instance;
}

/**
* @param ItemPricingInterface $pricing
* @return bool
*/
public function saveBusinessPricing(ItemPricingInterface $pricing): bool
{
$data = $pricing->serialize();
$currency = $pricing->getCurrency();
$currencyCode = $currency->get('alpha_code');

$rawPricing = $this->getRawBusinessPricing();
$rawPricing[$currencyCode] = $data;
$this->setRawBusinessPricing($rawPricing);
return $this->save();
}

/**
* @param array $rawValue
* @param comCurrency $currency
* @return ProductPricing|null
*/
protected function loadPricingInstance(array $rawValue, comCurrency $currency): ?ProductPricing
{
$instance = null;
$currencyCode = $currency->get('alpha_code');
if (array_key_exists($currencyCode, $rawValue)) {
try {
$instance = ProductPricing::unserialize(
$currency,
is_array($rawValue[$currencyCode])
? $rawValue[$currencyCode]
: (array)json_decode($rawValue[$currencyCode], true)
);
} catch (InvalidPriceTypeException $e) {
$this->adapter->log(
1,
'Could not create Pricing instance for product '
. $this->get('id') . ' with currency ' . $currencyCode . ', received ' . get_class($e)
. ' with message: ' . $e->getMessage() . ' // ' . $e->getTraceAsString()
);
}
}

return $instance;
}

/**
* Retrieves either normal pricing instance, or business pricing instance, based on is_inclusive or session value
* @param comCurrency $currency
* @return ProductPricing|null
*/
protected function getPricingInstance(comCurrency $currency): ?ProductPricing
{
// 1. Check for comOrder::is_inclusive
$order = null;
if (isset($_SESSION[comOrder::SESSION_NAME]) && $_SESSION[comOrder::SESSION_NAME] > 0) {
// Check the session for an ID
$orderId = (int)$_SESSION[comOrder::SESSION_NAME];
$order = $this->adapter->getObject(comOrder::class, [
'id' => $orderId,
'test' => $this->commerce->isTestMode(),
]);
} elseif (isset($_COOKIE[comOrder::COOKIE_NAME]) && !empty($_COOKIE[comOrder::COOKIE_NAME])) {
// Check the cookies for a more complex secret
$orderSecret = (string)$_COOKIE[comOrder::COOKIE_NAME];
$order = $this->adapter->getObject(comOrder::class, [
'secret' => $orderSecret,
'test' => $this->commerce->isTestMode(),
]);
}
if ($order instanceof comOrder) {
$taxMode = $order->get('is_inclusive') ? 'inclusive' : 'exclusive';
}

// 2. As a backup, check for dynamic tax mode inclusive/exclusive session value
if (empty($taxMode)) {
$taxMode = $_SESSION[$this->sessionKey] ?? '';
}

$raw = $taxMode === 'inclusive' ? $this->getRawBusinessPricing() : $this->getRawPricing();

return $this->loadPricingInstance($raw, $currency);
}

/**
* Retrieve normal pricing instance
* @param comCurrency $currency
* @return ProductPricing|null
*/
protected function getNormalPricingInstance(comCurrency $currency): ?ProductPricing
{
return $this->checkPricingInstance(
$currency,
$this->loadPricingInstance($this->getRawPricing(), $currency),
);
}

/**
* Retrieve business pricing instance
* @param comCurrency $currency
* @return ProductPricing|null
*/
protected function getBusinessPricingInstance(comCurrency $currency): ?ProductPricing
{
return $this->checkPricingInstance(
$currency,
$this->loadPricingInstance($this->getRawBusinessPricing(), $currency),
);
}

/**
* Make sure we have a pricing instance
* @param comCurrency $currency
* @param ProductPricing|null $instance
* @return ProductPricing
*/
protected function checkPricingInstance(comCurrency $currency, ProductPricing $instance = null): ProductPricing
{
if (!$instance instanceof ItemPricingInterface) {
$legacyPrice = $this->getPrice();
$newPrice = new \modmore\Commerce\Pricing\Price($currency, $legacyPrice->getPriceForCurrency($currency));
$instance = new ProductPricing($currency, $newPrice);
}

return $instance;
}

/**
* @return array
*/
public function getRawBusinessPricing(): array
{
return $this->getProperty('pricing_business') ?? [];
}

/**
* Stores the raw per-currency business pricing information.
* @param array $pricing
*/
protected function setRawBusinessPricing(array $pricing)
{
$this->setProperty('pricing_business', json_encode($pricing));
}

/**
* @return array
*/
public function getModelFields(): array
{
$fields = parent::getModelFields();

$enabledCurrencies = $this->commerce->getEnabledCurrencies();
/** @var ItemPricingInterface $pricing */
$normalPricing = [];
$businessPricing = [];
foreach ($enabledCurrencies as $currencyCode => $currency) {
$normalPricing[$currencyCode] = $this->getNormalPricingInstance($currency);
$businessPricing[$currencyCode] = $this->getBusinessPricingInstance($currency);
}
foreach ($fields as $k => $field) {
if ($field->getName() === 'pricing') {
// The reason we're overriding the normal pricing field as well is because getPricingInstance()
// for this product is dynamic. Here, we ALWAYS want the normal price.
$normalPricingField = new PricingField($this->commerce, [
'name' => 'pricing',
'label' => $this->adapter->lexicon('commerce.price'),
'pricing' => $normalPricing,
]);
$businessPricingField = new PricingField($this->commerce, [
'name' => 'properties[pricing_business]',
'label' => $this->adapter->lexicon('commerce_dynamictaxmode.business_price'),
'pricing' => $businessPricing,
]);

// Overwrite the existing pricing field, replacing it with the above two.
array_splice($fields, $k, 1, [$normalPricingField, $businessPricingField]);
break;
}
}

return $fields;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

$xpdo_meta_map = array (
'comProduct' =>
array (
0 => 'DynamicTaxModeProduct',
),
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php
require_once strtr(realpath(dirname(dirname(__FILE__))), '\\', '/') . '/dynamictaxmodeproduct.class.php';
/**
* Dynamictaxmode for Commerce.
*
* Copyright 2020 by Your Name <your@email.com>
*
* This file is meant to be used with Commerce by modmore. A valid Commerce license is required.
*
* @package commerce_dynamictaxmode
* @license See core/components/commerce_dynamictaxmode/docs/license.txt
*/
class DynamicTaxModeProduct_mysql extends DynamicTaxModeProduct
{

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php
/**
* Dynamictaxmode for Commerce.
*
* Copyright 2020 by Your Name <your@email.com>
*
* This file is meant to be used with Commerce by modmore. A valid Commerce license is required.
*
* @package commerce_dynamictaxmode
* @license See core/components/commerce_dynamictaxmode/docs/license.txt
*/

$xpdo_meta_map['DynamicTaxModeProduct']= array (
'package' => 'commerce_dynamictaxmode',
'version' => '1.1',
'extends' => 'comProduct',
'tableMeta' =>
array (
'engine' => 'InnoDB',
),
'fields' =>
array (
),
'fieldMeta' =>
array (
),
);
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<model package="commerce_dynamictaxmode" baseClass="comSimpleObject" platform="mysql" defaultEngine="InnoDB" version="1.1">
<object class="DynamicTaxModeProduct" extends="comProduct" />
<!--<object class="DynamictaxmodeOrderMessage" extends="comOrderMessage" />-->
<!--<object class="DynamictaxmodeProduct" extends="comProduct" />-->
<!--<object class="DynamictaxmodeShippingMethod" extends="comShippingMethod" />-->
</model>
10 changes: 8 additions & 2 deletions core/components/commerce_dynamictaxmode/src/Module.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

class Module extends BaseModule
{
public const DEFAULT_SESSION_KEY = 'commerce_dynamictaxmode';

public function getName(): string
{
$this->adapter->loadLexicon('commerce_dynamictaxmode:default');
Expand All @@ -35,15 +37,19 @@ public function initialize(EventDispatcher $dispatcher): void
// Load our lexicon
$this->adapter->loadLexicon('commerce_dynamictaxmode:default');

$root = dirname(__DIR__);
$path = $root . '/model/';
$this->adapter->loadPackage('commerce_dynamictaxmode', $path);

$dispatcher->addListener(\Commerce::EVENT_ORDER_BEFORE_LOAD, [$this, 'setDynamicTaxMode']);

// Add composer libraries to the about section
$dispatcher->addListener(\Commerce::EVENT_DASHBOARD_LOAD_ABOUT, [$this, 'addLibrariesToAbout']);
}

public function setDynamicTaxMode(Order $event)
public function setDynamicTaxMode(Order $event): void
{
$sessionKey = 'commerce_dynamictaxmode';
$sessionKey = self::DEFAULT_SESSION_KEY;
$module = $this->adapter->getObject(\comModule::class, [
'class_name' => 'modmore\Commerce_DynamicTaxMode\Module',
]);
Expand Down

0 comments on commit 4c30e3b

Please sign in to comment.