Quelques nouveaux fichiers et modules en conf

This commit is contained in:
2021-01-25 18:42:52 +01:00
parent 9b4d5e339b
commit af1552b390
212 changed files with 38073 additions and 817 deletions

View File

55
.docker/nginx/nginx.conf Normal file
View File

@@ -0,0 +1,55 @@
server {
listen 80;
root /application/web/;
index index.php;
access_log /var/log/nginx/starter.tld_access.log;
error_log /var/log/nginx/starter.tld_error.log;
location / {
try_files $uri $uri/ @rewriteapp;
}
location @rewriteapp {
# rewrite all to index.php
rewrite ^(.*)$ /index.php/$1 last;
}
# Php configuration
location ~ ^/(index|index_dev)\.php(/|$) {
# Php-FPM Config (Socks or Network)
#fastcgi_pass unix:/var/run/php/php7.1-fpm.sock;
fastcgi_pass php-fpm:9000;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_read_timeout 3000;
}
# Security. discard all files and folders starting with a "."
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# Stuffs
location = /favicon.ico {
allow all;
access_log off;
log_not_found off;
}
location ~ /robots.txt {
allow all;
access_log off;
log_not_found off;
}
# Static files
location ~* ^.+\.(jpg|jpeg|gif|css|png|js|pdf|zip)$ {
expires 30d;
access_log off;
log_not_found off;
}
}

View File

@@ -0,0 +1,19 @@
FROM phpdockerio/php73-fpm:latest
WORKDIR "/application"
# Fix debconf warnings upon build
ARG DEBIAN_FRONTEND=noninteractive
# Install selected extensions and other stuff
RUN apt-get update \
&& apt-get -y --no-install-recommends install php-mysql php7.3-mysql php7.3-gd php-imagick php7.3-intl php-yaml php-xdebug \
&& apt-get clean; rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/*
# Install git
RUN apt-get update \
&& apt-get -y install git \
&& apt-get clean; rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/*
RUN apt-get update \
&& apt-get -y install vim \
&& apt-get clean;

View File

@@ -0,0 +1,17 @@
upload_max_filesize = 100M
post_max_size = 108M
html_errors=On
display_errors=On
date.timezone=Europe/Paris
memory_limit=-1
max_execution_time = 300
xdebug.idekey = "PHPSTORM"
xdebug.remote_enable=1
xdebug.remote_autostart=1
xdebug.remote_port=9000
xdebug.remote_connect_back=1
xdebug.remote_handler=dbgp
xdebug.profiler_enable=0
xdebug.profiler_enable_trigger=1
xdebug.profiler_output_dir="/application/log"

65
.github/workflows/test.yml vendored Normal file
View File

@@ -0,0 +1,65 @@
name: test
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
phpunit:
name: PHP ${{ matrix.php-versions }}
runs-on: ubuntu-latest
services:
mysql:
image: mariadb:10.3
env:
MYSQL_ROOT_PASSWORD: thelia
MYSQL_DATABASE: thelia
ports:
- 3306/tcp
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
strategy:
fail-fast: false
matrix:
php-versions: ['5.6', '7.0', '7.1', '7.2', '7.3']
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Environment
run: |
sudo apt-get update
sudo apt-get install sendmail
- name: Setup PHP, with composer and extensions
uses: shivammathur/setup-php@v2 #https://github.com/shivammathur/setup-php
with:
php-version: ${{ matrix.php-versions }}
ini-values: post_max_size=20M
extensions: mbstring, xml, ctype, iconv, intl, pdo_sqlite, mysql, gd, zip
- name: Start mysql service
run: sudo /etc/init.d/mysql start
- name: Get composer cache directory
id: composercache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache composer dependencies
uses: actions/cache@v2
with:
path: ${{ steps.composercache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.
# key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install Composer dependencies
run: |
composer install --no-progress --prefer-dist --optimize-autoloader
composer update --no-interaction --prefer-dist
- name: Install Thelia
run: |
php Thelia thelia:install --db_host=127.0.0.1:${{ job.services.mysql.ports['3306'] }} --db_username=root --db_name=thelia --db_password=thelia
php setup/faker.php -r 0
php Thelia module:refresh
php Thelia module:activate Colissimo
php Thelia module:activate Cheque
php Thelia module:activate HookTest
php Thelia module:activate VirtualProductDelivery
php Thelia admin:create --login_name thelia2 --password thelia2 --last_name thelia2 --first_name thelia2 --email thelia2@example.com
- name: Run PHPUnit
run: php bin/phpunit

33
.gitignore vendored
View File

@@ -35,36 +35,6 @@ local/I18n/*.php
.docker/mysql-data/* .docker/mysql-data/*
!.docker/mysql-data/.gitkeep !.docker/mysql-data/.gitkeep
# Ignore everything in the "modules" directory, except the "default modules"
local/modules/*
!local/modules/Cheque/
!local/modules/Carousel/
!local/modules/Front/
!local/modules/FreeOrder/
!local/modules/Tinymce/
!local/modules/Colissimo/
!local/modules/Hook*/
!local/modules/VirtualProductDelivery/
!local/modules/VirtualProductControl/
!local/modules/TheliaSmarty/
!local/modules/TheliaMigrateCountry/
local/modules/HookTest/
# Ignore everything in the "templates" directory, except the "default template"
templates/*
!templates/frontOffice
templates/frontOffice/*
!templates/frontOffice/default
!templates/backOffice
templates/backOffice/*
!templates/backOffice/default
!templates/email
templates/email/*
!templates/email/default
!templates/pdf
templates/pdf/*
!templates/pdf/default
#Ignore CodeKit #Ignore CodeKit
codekit-config.json codekit-config.json
config.codekit config.codekit
@@ -76,6 +46,3 @@ config.codekit
# Ignore casperjs screenshots # Ignore casperjs screenshots
tests/functionnal/casperjs/screenshot/ tests/functionnal/casperjs/screenshot/
tests/phpunit/Thelia/Tests/Api/fixtures/visuel*.png tests/phpunit/Thelia/Tests/Api/fixtures/visuel*.png
/.docker/
/.github/
/.well-known/

View File

@@ -1,3 +1,3 @@
#<IfModule mod_alias.c> <IfModule mod_alias.c>
#RedirectMatch 301 ^/$ https://auxbieauxlegumes.fr/web RedirectMatch 301 ^/$ https://auxbieauxlegumes.fr/web
#</IfModule> </IfModule>

View File

@@ -62,9 +62,7 @@
"symfony/polyfill-php73": "^1.0", "symfony/polyfill-php73": "^1.0",
"symfony/lock": "^3.4|^4.0", "symfony/lock": "^3.4|^4.0",
"thelia/propel": "dev-thelia-2.4", "thelia/propel": "dev-thelia-2.4",
"symfony/var-dumper": "^2.0|^3.0|^4.0", "symfony/var-dumper": "^2.0|^3.0|^4.0"
"thelia/colissimo-module": "^2.4",
"thelia/smarty-module": "^2.4"
}, },
"require-dev": { "require-dev": {
"fzaninotto/faker": "1.5.*", "fzaninotto/faker": "1.5.*",

1326
composer.lock generated

File diff suppressed because it is too large Load Diff

363
local/modules/Atos/Atos.php Normal file
View File

@@ -0,0 +1,363 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Atos;
use Atos\Model\AtosCurrencyQuery;
use Propel\Runtime\ActiveQuery\Criteria;
use Propel\Runtime\Connection\ConnectionInterface;
use Symfony\Component\Routing\Router;
use Thelia\Core\HttpFoundation\Response;
use Thelia\Core\Template\ParserInterface;
use Thelia\Core\Translation\Translator;
use Thelia\Install\Database;
use Thelia\Log\Tlog;
use Thelia\Model\Config;
use Thelia\Model\ConfigQuery;
use Thelia\Model\Message;
use Thelia\Model\MessageQuery;
use Thelia\Model\Order;
use Thelia\Module\AbstractPaymentModule;
use Thelia\Tools\URL;
class Atos extends AbstractPaymentModule
{
const MODULE_DOMAIN = 'atos';
/**
* The confirmation message identifier
*/
const CONFIRMATION_MESSAGE_NAME = 'atos_payment_confirmation';
private $parameters;
public function postActivation(ConnectionInterface $con = null)
{
// Setup some default values
if (null === Atos::getConfigValue('atos_merchantId', null)) {
Atos::setConfigValue('atos_transactionId', 1);
Atos::setConfigValue('minimum_amount', 0);
Atos::setConfigValue('maximum_amount', 0);
Atos::setConfigValue('send_payment_confirmation_message', 1);
}
// Try to chmod binaries if they're not executables
$binFile = Atos::getBinDirectory() . 'request';
if (! is_executable($binFile)) {
@chmod($binFile, 0755);
}
$binFile = Atos::getBinDirectory() . 'response';
if (! is_executable($binFile)) {
@chmod($binFile, 0755);
}
$database = new Database($con);
$database->insertSql(null, array(
__DIR__ . DS . 'Config'.DS.'thelia.sql'
));
// Create payment confirmation message from templates, if not already defined
$email_templates_dir = __DIR__.DS.'I18n'.DS.'email-templates'.DS;
if (null === MessageQuery::create()->findOneByName(Atos::CONFIRMATION_MESSAGE_NAME)) {
$message = new Message();
$message
->setName(Atos::CONFIRMATION_MESSAGE_NAME)
->setHtmlTemplateFileName('atos-payment-confirmation.html')
->setTextTemplateFileName('atos-payment-confirmation.txt')
->setLocale('en_US')
->setTitle('Atos payment confirmation')
->setSubject('Payment of order {$order_ref}')
->setLocale('fr_FR')
->setTitle('Confirmation de paiement par Atos')
->setSubject('Confirmation du paiement de votre commande {$order_ref}')
->save()
;
}
$this->replacePath();
}
public function update($currentVersion, $newVersion, ConnectionInterface $con = null)
{
// Migrate old configuration
if (null === Atos::getConfigValue('atos_merchantId', null)) {
if (null !== $atosConfigs = ConfigQuery::create()->filterByName('atos_%', Criteria::LIKE)->find()) {
/** @var Config $atosConfig */
foreach ($atosConfigs as $atosConfig) {
Atos::setConfigValue($atosConfig->getName(), $atosConfig->getValue());
$atosConfig->delete($con);
}
}
}
parent::update($currentVersion, $newVersion, $con);
}
public function destroy(ConnectionInterface $con = null, $deleteModuleData = false)
{
if ($deleteModuleData) {
$database = new Database($con);
$database->execute('drop table `atos_currency`');
MessageQuery::create()->findOneByName(Atos::CONFIRMATION_MESSAGE_NAME)->delete();
}
}
protected function replacePath()
{
$pathfile = $this->getPathfilePath();
$pathfileContent = @file_get_contents($pathfile . '.dist');
if ($pathfileContent) {
$pathfileContent = str_replace('__PATH__', __DIR__, $pathfileContent);
if (! file_put_contents($this->getConfigDirectory() . 'pathfile', $pathfileContent)) {
throw new \RuntimeException(
Translator::getInstance()->trans(
'File %file must be writable, please check Atos/Config directory permissions.',
[ '%file' => 'pathfile' ],
Atos::MODULE_DOMAIN
)
);
}
} else {
throw new \RuntimeException(
Translator::getInstance()->trans(
'Failed to read the %file file. Please check file and directory permissions.',
[ '%file' => $pathfile . '.dist' ],
Atos::MODULE_DOMAIN
)
);
}
}
/**
* @param string $key atos key parameter
* @param string $value parameter value
* @return $this
*/
protected function addParam($key, $value)
{
$this->parameters = sprintf("%s %s=%s", $this->parameters, $key, $value);
return $this;
}
protected function getParameters()
{
return trim($this->parameters);
}
/**
*
* generate a transaction id for atos solution
*
* @return int|mixed
*/
private function generateTransactionID()
{
$transId = Atos::getConfigValue('atos_transactionId', 1);
$transId = 1 + intval($transId);
if (strlen($transId) > 6) {
$transId = 1;
}
Atos::setConfigValue('atos_transactionId', $transId);
return sprintf("%06d", $transId);
}
/**
*
* Method used by payment gateway.
*
* If this method return a \Thelia\Core\HttpFoundation\Response instance, this response is send to the
* browser.
*
* In many cases, it's necessary to send a form to the payment gateway.
* On your response you can return this form already completed, ready to be sent
*
* @param \Thelia\Model\Order $order processed order
* @return null|\Thelia\Core\HttpFoundation\Response
*/
public function pay(Order $order)
{
$pathBin = Atos::getBinDirectory() .'request';
$atosCurrency = AtosCurrencyQuery::create()->findPk(
$order->getCurrency()->getCode()
);
if (null == $atosCurrency) {
throw new \InvalidArgumentException(
sprintf(
"Atos does not supprot this currency : %s",
$order->getCurrency()->getCode()
)
);
}
$amount = $order->getTotalAmount();
$amount = number_format($amount, $atosCurrency->getDecimals(), '', '');
$transactionId = $this->generateTransactionID();
$order->setTransactionRef($transactionId)->save();
/** @var Router $router */
$router = $this->getContainer()->get('router.atos');
$this
->addParam('pathfile', Atos::getPathfilePath())
->addParam('merchant_id', Atos::getConfigValue('atos_merchantId'))
->addParam('customer_email', $order->getCustomer()->getEmail())
->addParam('currency_code', $atosCurrency->getAtosCode())
->addParam('amount', $amount)
->addParam('language', $order->getLang()->getCode())
->addParam('transaction_id', $transactionId)
->addParam('order_id', $order->getId())
->addParam('automatic_response_url', URL::getInstance()->absoluteUrl($router->generate('atos.payment.confirmation')))
->addParam('cancel_return_url', URL::getInstance()->absoluteUrl($router->generate('atos.payment.cancel', [ 'orderId' => $order->getId() ])))
->addParam('normal_return_url', $this->getPaymentSuccessPageUrl($order->getId()))
;
$encrypt = exec(sprintf("%s %s", $pathBin, $this->getParameters()));
if (! empty($encrypt)) {
$datas = explode('!', $encrypt);
if ($datas[1] == '' && $datas[2] == '') {
throw new \RuntimeException(
Translator::getInstance()->trans('Request binary not found in "%path"', ['%path' => $pathBin])
);
} elseif ($datas[1] != 0) {
throw new \RuntimeException($datas[2]);
} else {
/** @var ParserInterface $parser */
$parser = $this->getContainer()->get('thelia.parser');
$parser->setTemplateDefinition(
$parser->getTemplateHelper()->getActiveFrontTemplate(),
true
);
$content = $parser->render('atos/payment.html', [
'site_name' => self::getConfigValue('store_name'),
'form' => $datas[3],
'order_id' => $order->getId()
]);
return Response::create($content);
}
} else {
throw new \RuntimeException(
Translator::getInstance()->trans(
'Empty response recevied from Atos binary "%path". Please check path and permissions.',
['%path' => $pathBin],
Atos::MODULE_DOMAIN
)
);
// FIXME : show something to the customer
}
}
/**
* @return boolean true to allow usage of this payment module, false otherwise.
*/
public function isValidPayment()
{
$valid = false;
// Check config files
$parmcomFile = Atos::getConfigDirectory() . 'parmcom.' . Atos::getConfigValue('atos_merchantId', '0');
$certifFile = Atos::getConfigDirectory() . 'certif.fr.' . Atos::getConfigValue('atos_merchantId', '0');
if (is_readable($parmcomFile) && is_readable($certifFile)) {
$mode = Atos::getConfigValue('atos_mode', false);
// If we're in test mode, do not display Payzen on the front office, except for allowed IP addresses.
if ('TEST' == $mode) {
$raw_ips = explode("\n", Atos::getConfigValue('atos_allowed_ip_list', ''));
$allowed_client_ips = array();
foreach ($raw_ips as $ip) {
$allowed_client_ips[] = trim($ip);
}
$client_ip = $this->getRequest()->getClientIp();
$valid = in_array($client_ip, $allowed_client_ips);
} elseif ('PRODUCTION' == $mode) {
$valid = true;
}
if ($valid) {
// Check if total order amount is in the module's limits
$valid = $this->checkMinMaxAmount();
}
} else {
Tlog::getInstance()->addWarning(
Translator::getInstance()->trans(
"Atos payment module is nort properly configured. Please check module configuration in your back-office.",
[],
Atos::MODULE_DOMAIN
)
);
}
return $valid;
}
/**
* Check if total order amount is in the module's limits
*
* @return bool true if the current order total is within the min and max limits
*/
protected function checkMinMaxAmount()
{
// Check if total order amount is in the module's limits
$order_total = $this->getCurrentOrderTotalAmount();
$min_amount = Atos::getConfigValue('atos_minimum_amount', 0);
$max_amount = Atos::getConfigValue('atos_maximum_amount', 0);
return
$order_total > 0
&&
($min_amount <= 0 || $order_total >= $min_amount) && ($max_amount <= 0 || $order_total <= $max_amount);
}
public static function getBinDirectory()
{
return __DIR__ . DS . 'bin' . DS;
}
public static function getConfigDirectory()
{
return __DIR__ . DS . 'Config' . DS;
}
public static function getPathfilePath()
{
return Atos::getConfigDirectory() . 'pathfile';
}
}

View File

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" ?>
<config xmlns="http://thelia.net/schema/dic/config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://thelia.net/schema/dic/config http://thelia.net/schema/dic/config/thelia-1.0.xsd">
<forms>
<form name="atos_configuration" class="Atos\Form\ConfigForm" />
</forms>
<services>
<service id="atos.confirmation.email" class="Atos\EventListeners\SendConfirmationEmail" scope="request">
<argument type="service" id="mailer"/>
<tag name="kernel.event_subscriber"/>
</service>
</services>
<hooks>
<hook id="atos.configuration.hook" class="Atos\Hook\HookManager" scope="request">
<tag name="hook.event_listener" event="module.configuration" type="back" method="onModuleConfigure" />
</hook>
</hooks>
</config>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<module>
<fullnamespace>Atos\Atos</fullnamespace>
<descriptive locale="en_US">
<title>Atos-SIPS payment module</title>
</descriptive>
<descriptive locale="fr_FR">
<title>Module de paiement Atos-SIPS</title>
</descriptive>
<version>1.2.1</version>
<author>
<name>Manuel Raynaud, Franck Allimant</name>
<email>manu@thelia.net, franck@cqfdev.fr</email>
</author>
<type>payment</type>
<thelia>2.1.0</thelia>
<stability>prod</stability>
</module>

View File

@@ -0,0 +1,39 @@
# couleur du fond d'ecran (blanc)
BGCOLOR!ffffff!
# Mode d'affichage des blocs de paiment
BLOCK_ALIGN!center!
# Ordre d'affichage des blocs de paiement
BLOCK_ORDER!1,2,3,4,5,6,7,8!
# Mode de securite
CONDITION!SSL!
# flag d'edition des libelles des blocs de paiement
HEADER_FLAG!yes!
# Code langage de l'acheteur (fr=francais)
LANGUAGE!fr!
# Logo ATOS paiement
#LOGO!logo.gif!
# Logo Banque Populaire
#LOGO2!logo.gif!
# Code pays du commercant
MERCHANT_COUNTRY!fr!
# Code langage du commercant
MERCHANT_LANGUAGE!fr!
# Liste des moyens de paiement acceptes
PAYMENT_MEANS!CB,2,VISA,2,MASTERCARD,2!
# Passage en une seule frame securisee au moment du paiement
TARGET!_top!
# Couleur du text (noir)
TEXTCOLOR!000000!
# END OF FILE

View File

@@ -0,0 +1,15 @@
###############################################################################
#
# Fichier des parametres du commercant
#
# Remarque : Ce fichier parametre est sous la responsabilite du
# commercant
#
###############################################################################
# Logo du commercant (il apparait en entete sur les pages de paiement)
#ADVERT!merchant.gif!
# END OF FILE

View File

@@ -0,0 +1,31 @@
#########################################################################
#
# Pathfile
#
# Liste fichiers parametres utilisés par le module de paiement
#
#########################################################################
# ------------------------------------------------------------------------
# Chemin vers le répertoire des logos depuis le web alias
# ------------------------------------------------------------------------
#
D_LOGO!/atos/logo/!
#
#------------------------------------------------------------------------
#------------------------------------------------------------------------
#
# certificat du commercant
#
F_CERTIFICATE!/home/pala4545/public_html/web/local/modules/Atos/Config/certif!
#
# fichier paramètre commercant
#
F_PARAM!/home/pala4545/public_html/web/local/modules/Atos/Config/parmcom!
#
# fichier des paramètres ATOS
#
F_DEFAULT!/home/pala4545/public_html/web/local/modules/Atos/Config/parmcom.atos!
#
# --------------------------------------------------------------------------
# end of file
# --------------------------------------------------------------------------

View File

@@ -0,0 +1,31 @@
#########################################################################
#
# Pathfile
#
# Liste fichiers parametres utilisés par le module de paiement
#
#########################################################################
# ------------------------------------------------------------------------
# Chemin vers le répertoire des logos depuis le web alias
# ------------------------------------------------------------------------
#
D_LOGO!/atos/logo/!
#
#------------------------------------------------------------------------
#------------------------------------------------------------------------
#
# certificat du commercant
#
F_CERTIFICATE!__PATH__/Config/certif!
#
# fichier paramètre commercant
#
F_PARAM!__PATH__/Config/parmcom!
#
# fichier des paramètres ATOS
#
F_DEFAULT!__PATH__/Config/parmcom.atos!
#
# --------------------------------------------------------------------------
# end of file
# --------------------------------------------------------------------------

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
<route id="atos.config" path="/admin/module/atos/configure" methods="post">
<default key="_controller">Atos\Controller\ConfigureController::configure</default>
</route>
<route id="atos.download.log" path="/admin/module/atos/log">
<default key="_controller">Atos\Controller\ConfigureController::downloadLog</default>
</route>
<route id="atos.logo" path="atos/logo/{image}">
<default key="_controller">Atos\Controller\PaymentController::displayLogo</default>
</route>
<route id="atos.payment.confirmation" path="atos/callback" methods="post">
<default key="_controller">Atos\Controller\PaymentController::processAtosRequest</default>
</route>
<route id="atos.payment.cancel" path="atos/cancel/{orderId}">
<default key="_controller">Atos\Controller\PaymentController::processUserCancel</default>
<requirement key="orderId">\d+</requirement>
</route>
</routes>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<database defaultIdMethod="native" name="thelia" namespace="Atos\Model">
<!--
See propel documentation on http://propelorm.org for all information about schema file
-->
<table name="atos_currency">
<column name="code" required="true" size="128" type="VARCHAR" primaryKey="true"/>
<column name="atos_code" type="INTEGER" />
<column name="decimals" type="INTEGER" />
</table>
<external-schema filename="local/config/schema.xml" referenceOnly="true" />
</database>

View File

@@ -0,0 +1,44 @@
# This is a fix for InnoDB in MySQL >= 4.1.x
# It "suspends judgement" for fkey relationships until are tables are set.
SET FOREIGN_KEY_CHECKS = 0;
-- ---------------------------------------------------------------------
-- atos_currency
-- ---------------------------------------------------------------------
DROP TABLE IF EXISTS `atos_currency`;
CREATE TABLE `atos_currency`
(
`code` VARCHAR(128) NOT NULL,
`atos_code` INTEGER,
`decimals` INTEGER,
PRIMARY KEY (`code`)
) ENGINE=InnoDB;
INSERT INTO `atos_currency`(`code`,`atos_code`,`decimals`) VALUES
('EUR', '978', 2),
('USD', '840', 2),
('CHF', '756', 2),
('GBP', '826', 2),
('CAD', '124', 2),
('JPY', '392', 0),
('MXN', '484', 2),
('TRY', '949', 2),
('AUD', '036', 2),
('NZD', '554', 2),
('NOK', '578', 2),
('BRL', '986', 2),
('ARS', '032', 2),
('KHR', '116', 2),
('TWD', '901', 2),
('SEK', '752', 2),
('DKK', '208', 2),
('KRW', '410', 0),
('SGD', '702', 2),
('XPF', '953', 2),
('XAF', '952', 2);
# This restores the fkey checks, after having unset them earlier
SET FOREIGN_KEY_CHECKS = 1;

View File

@@ -0,0 +1,166 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Atos\Controller;
use Atos\Atos;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpFoundation\Response;
use Thelia\Controller\Admin\BaseAdminController;
use Thelia\Core\Security\AccessManager;
use Thelia\Core\Security\Resource\AdminResources;
use Thelia\Core\Thelia;
use Thelia\Exception\FileException;
use Thelia\Form\Exception\FormValidationException;
use Thelia\Tools\URL;
use Thelia\Tools\Version\Version;
/**
* Class ConfigureController
* @package Atos\Controller
* @author manuel raynaud <mraynaud@openstudio.fr>, Franck Allimant <franck@cqfdev.fr>
*/
class ConfigureController extends BaseAdminController
{
public function copyDistFile($fileName, $merchantId)
{
$distFile = Atos::getConfigDirectory() . $fileName . '.dist';
$destFile = Atos::getConfigDirectory() . $fileName . '.' . $merchantId;
if (! is_readable($destFile)) {
if (!is_file($distFile) && !is_readable($distFile)) {
throw new FileException(sprintf("Can't read file '%s', please check file permissions", $distFile));
}
// Copy the dist file in place
$fs = new Filesystem();
$fs->copy($distFile, $destFile);
}
return $destFile;
}
public function checkExecutable($fileName)
{
$binFile = Atos::getBinDirectory() . $fileName;
if (! is_executable($binFile)) {
throw new FileException(
$this->getTranslator()->trans(
"The '%file' should be executable. Please check file permission",
[ '%file' => $binFile ],
Atos::MODULE_DOMAIN
)
);
}
}
public function downloadLog()
{
if (null !== $response = $this->checkAuth(AdminResources::MODULE, 'atos', AccessManager::UPDATE)) {
return $response;
}
$logFilePath = sprintf(THELIA_ROOT."log".DS."%s.log", Atos::MODULE_DOMAIN);
return Response::create(
@file_get_contents($logFilePath),
200,
array(
'Content-type' => "text/plain",
'Content-Disposition' => sprintf('Attachment;filename=atos-log.txt')
)
);
}
public function configure()
{
if (null !== $response = $this->checkAuth(AdminResources::MODULE, 'atos', AccessManager::UPDATE)) {
return $response;
}
$configurationForm = $this->createForm('atos_configuration');
$message = null;
try {
$form = $this->validateForm($configurationForm);
// Get the form field values
$data = $form->getData();
foreach ($data as $name => $value) {
if (is_array($value)) {
$value = implode(';', $value);
}
Atos::setConfigValue($name, $value);
}
$merchantId = $data['atos_merchantId'];
$this->checkExecutable('request');
$this->checkExecutable('response');
$this->copyDistFile('parmcom', $merchantId);
$certificateFile = $this->copyDistFile('certif.fr', $merchantId);
// Write certificate
if (! @file_put_contents($certificateFile, $data['atos_certificate'])) {
throw new FileException(
$this->getTranslator()->trans(
"Failed to write certificate data in file '%file'. Please check file permission",
[ '%file' => $certificateFile ],
Atos::MODULE_DOMAIN
)
);
}
// Log configuration modification
$this->adminLogAppend(
"atos.configuration.message",
AccessManager::UPDATE,
"Atos configuration updated"
);
// Redirect to the success URL,
if ($this->getRequest()->get('save_mode') == 'stay') {
// If we have to stay on the same page, redisplay the configuration page/
$url = '/admin/module/Atos';
} else {
// If we have to close the page, go back to the module back-office page.
$url = '/admin/modules';
}
return $this->generateRedirect(URL::getInstance()->absoluteUrl($url));
} catch (FormValidationException $ex) {
$message = $this->createStandardFormValidationErrorMessage($ex);
} catch (\Exception $ex) {
$message = $ex->getMessage();
}
$this->setupFormErrorContext(
$this->getTranslator()->trans("Atos configuration", [], Atos::MODULE_DOMAIN),
$message,
$configurationForm,
$ex
);
// Before 2.2, the errored form is not stored in session
if (Version::test(Thelia::THELIA_VERSION, '2.2', false, "<")) {
return $this->render('module-configure', [ 'module_code' => 'Atos' ]);
} else {
return $this->generateRedirect(URL::getInstance()->absoluteUrl('/admin/module/Atos'));
}
}
}

View File

@@ -0,0 +1,252 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Atos\Controller;
use Atos\Atos;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Thelia\Core\Event\Order\OrderEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\HttpFoundation\Response;
use Thelia\Exception\TheliaProcessException;
use Thelia\Model\OrderQuery;
use Thelia\Model\OrderStatusQuery;
use Thelia\Module\BasePaymentModuleController;
/**
* Class PaymentController
* @package Atos\Controller
* @author manuel raynaud <mraynaud@openstudio.fr>, Franck Allimant <franck@cqfdev.fr>
*/
class PaymentController extends BasePaymentModuleController
{
public function processAtosRequest()
{
$this->getLog()->addInfo(
$this->getTranslator()->trans(
"Atos-SIPS platform request received.",
[],
Atos::MODULE_DOMAIN
)
);
$binResponse = Atos::getBinDirectory() . 'response';
if (! empty($_POST['DATA'])) {
$data = escapeshellcmd($_POST['DATA']);
$pathfile = Atos::getPathfilePath();
$resultRaw = exec(sprintf("%s message=%s pathfile=%s", $binResponse, $data, $pathfile));
if (!empty($resultRaw)) {
$result = explode('!', $resultRaw);
$result = $this->parseResult($result);
$this->getLog()->addInfo(
$this->getTranslator()->trans(
'Response parameters : %resp',
['%resp' => print_r($result, true)],
Atos::MODULE_DOMAIN
)
);
if ($result['code'] == '' && $result['error'] == '') {
$this->getLog()->addError(
$this->getTranslator()->trans(
'Response request not found in %response',
['%response' => $binResponse],
Atos::MODULE_DOMAIN
)
);
} elseif (intval($result['code']) != 0) {
$this->getLog()->addError(
$this->getTranslator()->trans(
'Error %code while processing response, with message %message',
['%code' => intval($result['code']), '%message' => $result['error']],
Atos::MODULE_DOMAIN
)
);
} elseif ($result['response_code'] == '00') {
$atos = new Atos();
$order = OrderQuery::create()
->filterByTransactionRef($result['transaction_id'])
->filterByPaymentModuleId($atos->getModuleModel()->getId())
->findOne();
if ($order) {
$this->confirmPayment($order->getId());
$this->getLog()->addInfo(
$this->getTranslator()->trans(
"Order ID %id is confirmed.",
['%id' => $order->getId()],
Atos::MODULE_DOMAIN
)
);
} else {
$this->getLog()->addError(
$this->getTranslator()->trans(
'Cannot find an order for transaction ID "%trans"',
['%trans' => $result['transaction_id']],
Atos::MODULE_DOMAIN
)
);
}
} else {
$this->getLog()->addError(
$this->getTranslator()->trans(
'Cannot validate order. Response code is %resp',
['%resp' => $result['response_code']],
Atos::MODULE_DOMAIN
)
);
}
} else {
$this->getLog()->addError(
$this->getTranslator()->trans(
'Got empty response from executable %binary, check path and permissions',
['%binary' => $binResponse],
Atos::MODULE_DOMAIN
)
);
}
} else {
$this->getLog()->addError(
$this->getTranslator()->trans(
'Request does not contains any data',
[],
Atos::MODULE_DOMAIN
)
);
}
$this->getLog()->info(
$this->getTranslator()->trans(
"Atos platform request processing terminated.",
[],
Atos::MODULE_DOMAIN
)
);
return Response::create();
}
/*
* @param $orderId int the order ID
* @return \Thelia\Core\HttpFoundation\Response
*/
public function processUserCancel($orderId)
{
$this->getLog()->addInfo(
$this->getTranslator()->trans(
'User canceled payment of order %id',
['%id' => $orderId],
Atos::MODULE_DOMAIN
)
);
try {
if (null !== $order = OrderQuery::create()->findPk($orderId)) {
$currentCustomerId = $this->getSecurityContext()->getCustomerUser()->getId();
$orderCustomerId = $order->getCustomerId();
if ($orderCustomerId != $currentCustomerId) {
throw new TheliaProcessException(
sprintf(
"User ID %d is trying to cancel order ID %d ordered by user ID %d",
$currentCustomerId,
$orderId,
$orderCustomerId
)
);
}
$event = new OrderEvent($order);
$event->setStatus(OrderStatusQuery::getCancelledStatus()->getId());
$this->dispatch(TheliaEvents::ORDER_UPDATE_STATUS, $event);
}
} catch (\Exception $ex) {
$this->getLog()->addError("Error occurred while canceling order ID $orderId: " . $ex->getMessage());
}
$this->redirectToFailurePage(
$orderId,
$this->getTranslator()->trans('you cancel the payment', [], Atos::MODULE_DOMAIN)
);
}
protected function parseResult($result)
{
return [
'code' => $result[1],
'error' => $result[2],
'merchant_id' => $result[3],
'merchant_country' => $result[4],
'amount' => $result[5],
'transaction_id' => $result[6],
'payment_means' => $result[7],
'transmission_date' => $result[8],
'payment_time' => $result[9],
'payment_date' => $result[10],
'response_code' => $result[11],
'payment_certificate' => $result[12],
'authorisation_id' => $result[13],
'currency_code' => $result[14],
'card_number' => $result[15],
'cvv_flag' => $result[16],
'cvv_response_code' => $result[17],
'bank_response_code' => $result[18],
'complementary_code' => $result[19],
'complementary_info' => $result[20],
'return_context' => $result[21],
'caddie' => $result[22],
'receipt_complement' => $result[23],
'merchant_language' => $result[24],
'language' => $result[25],
'customer_id' => $result[26],
'order_id' => $result[27],
'customer_email' => $result[28],
'customer_ip_address' => $result[29],
'capture_day' => $result[30],
'capture_mode' => $result[31],
'data' => $result[32]
];
}
public function displayLogo($image)
{
if (file_exists(__DIR__ . DS . '..' . DS . 'logo' . DS . $image)) {
$sourceImage = file_get_contents(__DIR__ . DS . '..' . DS . 'logo' . DS . $image);
return Response::create($sourceImage, 200, [
'Content-Type' => 'image/gif',
'Content-Length' => strlen($sourceImage)
]);
} else {
throw new NotFoundHttpException();
}
}
/**
* Return a module identifier used to calculate the name of the log file,
* and in the log messages.
*
* @return string the module code
*/
protected function getModuleCode()
{
return 'Atos';
}
}

View File

@@ -0,0 +1,94 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Atos\EventListeners;
use Atos\Atos;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Order\OrderEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Log\Tlog;
use Thelia\Mailer\MailerFactory;
/**
* Class SendEmailConfirmation
* @package Atos\EventListeners
* @author manuel raynaud <mraynaud@openstudio.fr>
* @author franck allimant <franck@cqfdev.fr>
*/
class SendConfirmationEmail implements EventSubscriberInterface
{
/**
* @var MailerFactory
*/
protected $mailer;
public function __construct(MailerFactory $mailer)
{
$this->mailer = $mailer;
}
/**
* @param OrderEvent $event
*
* @throws \Exception if the message cannot be loaded.
*/
public function sendConfirmationEmail(OrderEvent $event)
{
if (Atos::getConfigValue('send_confirmation_message_only_if_paid')) {
// We send the order confirmation email only if the order is paid
$order = $event->getOrder();
if (! $order->isPaid() && $order->getPaymentModuleId() == Atos::getModuleId()) {
$event->stopPropagation();
}
}
}
/*
* @params OrderEvent $order
* Checks if order payment module is paypal and if order new status is paid, send an email to the customer.
*/
public function updateStatus(OrderEvent $event)
{
$order = $event->getOrder();
if ($order->isPaid() && $order->getPaymentModuleId() == Atos::getModuleId()) {
if (Atos::getConfigValue('send_payment_confirmation_message')) {
$this->mailer->sendEmailToCustomer(
Atos::CONFIRMATION_MESSAGE_NAME,
$order->getCustomer(),
[
'order_id' => $order->getId(),
'order_ref' => $order->getRef()
]
);
}
// Send confirmation email if required.
if (Atos::getConfigValue('send_confirmation_message_only_if_paid')) {
$event->getDispatcher()->dispatch(TheliaEvents::ORDER_SEND_CONFIRMATION_EMAIL, $event);
}
Tlog::getInstance()->debug("Confirmation email sent to customer " . $order->getCustomer()->getEmail());
}
}
public static function getSubscribedEvents()
{
return array(
TheliaEvents::ORDER_UPDATE_STATUS => array("updateStatus", 128),
TheliaEvents::ORDER_SEND_CONFIRMATION_EMAIL => array("sendConfirmationEmail", 129)
);
}
}

View File

@@ -0,0 +1,249 @@
<?php
/*************************************************************************************/
/* This file is part of the Thelia package. */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace Atos\Form;
use Atos\Atos;
use Symfony\Component\Validator\Constraints\GreaterThanOrEqual;
use Symfony\Component\Validator\Constraints\NotBlank;
use Thelia\Core\Translation\Translator;
use Thelia\Form\BaseForm;
use Thelia\Model\Module;
use Thelia\Model\ModuleQuery;
/**
* Class Config
* @author manuel raynaud <mraynaud@openstudio.fr>
*/
class ConfigForm extends BaseForm
{
protected function buildForm()
{
// If the Multi plugin is not enabled, all multi_fields are hidden
/** @var Module $multiModule */
$multiEnabled = (null !== $multiModule = ModuleQuery::create()->findOneByCode('AtosNx')) && $multiModule->getActivate() != 0;
$translator = Translator::getInstance();
$this->formBuilder
->add(
'atos_merchantId',
'text',
[
'constraints' => [
new NotBlank(),
],
'label' => $translator->trans('Shop Merchant ID', [], Atos::MODULE_DOMAIN),
'label_attr' => [
'for' => 'merchant_id',
]
]
)
->add(
'atos_mode',
'choice',
[
'constraints' => [
new NotBlank()
],
'choices' => [
'TEST' => $translator->trans('Test', [], Atos::MODULE_DOMAIN),
'PRODUCTION' => $translator->trans('Production', [], Atos::MODULE_DOMAIN),
],
'label' => $translator->trans('Operation Mode', [], Atos::MODULE_DOMAIN),
'label_attr' => [
'for' => 'mode',
'help' => $translator->trans('Test or production mode', [], Atos::MODULE_DOMAIN)
]
]
)
->add(
'atos_allowed_ip_list',
'textarea',
[
'required' => false,
'label' => $translator->trans('Allowed IPs in test mode', [], Atos::MODULE_DOMAIN),
'label_attr' => [
'for' => 'platform_url',
'help' => $translator->trans(
'List of IP addresses allowed to use this payment on the front-office when in test mode (your current IP is %ip). One address per line',
[ '%ip' => $this->getRequest()->getClientIp() ],
Atos::MODULE_DOMAIN
)
],
'attr' => [
'rows' => 3
]
]
)
->add(
'atos_minimum_amount',
'text',
[
'constraints' => [
new NotBlank(),
new GreaterThanOrEqual(['value' => 0 ])
],
'label' => $translator->trans('Minimum order total', [], Atos::MODULE_DOMAIN),
'label_attr' => [
'for' => 'minimum_amount',
'help' => $translator->trans(
'Minimum order total in the default currency for which this payment method is available. Enter 0 for no minimum',
[],
Atos::MODULE_DOMAIN
)
]
]
)
->add(
'atos_maximum_amount',
'text',
[
'constraints' => [
new NotBlank(),
new GreaterThanOrEqual([ 'value' => 0 ])
],
'label' => $translator->trans('Maximum order total', [], Atos::MODULE_DOMAIN),
'label_attr' => [
'for' => 'maximum_amount',
'help' => $translator->trans(
'Maximum order total in the default currency for which this payment method is available. Enter 0 for no maximum',
[],
Atos::MODULE_DOMAIN
)
]
]
)
->add(
'atos_certificate',
'textarea',
[
'required' => false,
'label' => $translator->trans('ATOS certificate content', [], Atos::MODULE_DOMAIN),
'label_attr' => [
'for' => 'platform_url',
'help' => $translator->trans(
'Please paste here the certificate downloaded from the Atos SIPS platform',
[],
Atos::MODULE_DOMAIN
),
],
'attr' => [
'rows' => 10
]
]
)
->add(
'send_confirmation_message_only_if_paid',
'checkbox',
[
'value' => 1,
'required' => false,
'label' => $this->translator->trans('Send order confirmation on payment success', [], Atos::MODULE_DOMAIN),
'label_attr' => [
'help' => $this->translator->trans(
'If checked, the order confirmation message is sent to the customer only when the payment is successful. The order notification is always sent to the shop administrator',
[],
Atos::MODULE_DOMAIN
)
]
]
)
->add(
'send_payment_confirmation_message',
'checkbox',
[
'value' => 1,
'required' => false,
'label' => $this->translator->trans('Send a payment confirmation e-mail', [], Atos::MODULE_DOMAIN),
'label_attr' => [
'help' => $this->translator->trans(
'If checked, a payment confirmation e-mail is sent to the customer.',
[],
Atos::MODULE_DOMAIN
)
]
]
)
// -- Multiple times payement parameters, hidden id the AtosNx module is not activated.
->add(
'nx_nb_installments',
$multiEnabled ? 'text' : 'hidden',
[
'constraints' => [
new NotBlank(),
new GreaterThanOrEqual(['value' => 1 ])
],
'required' => $multiEnabled,
'label' => $translator->trans('Number of installments', [], Atos::MODULE_DOMAIN),
'label_attr' => [
'for' => 'nx_nb_installments',
'help' => $translator->trans(
'Number of installements. Should be more than one',
[],
Atos::MODULE_DOMAIN
)
]
]
)
->add(
'nx_minimum_amount',
$multiEnabled ? 'text' : 'hidden',
[
'constraints' => [
new NotBlank(),
new GreaterThanOrEqual(['value' => 0 ])
],
'required' => $multiEnabled,
'label' => $translator->trans('Minimum order total', [], Atos::MODULE_DOMAIN),
'label_attr' => [
'for' => 'nx_minimum_amount',
'help' => $translator->trans(
'Minimum order total in the default currency for which the multiple times payment method is available. Enter 0 for no minimum',
[],
Atos::MODULE_DOMAIN
)
]
]
)
->add(
'nx_maximum_amount',
$multiEnabled ? 'text' : 'hidden',
[
'constraints' => [
new NotBlank(),
new GreaterThanOrEqual([ 'value' => 0 ])
],
'required' => $multiEnabled,
'label' => $translator->trans('Maximum order total', [], Atos::MODULE_DOMAIN),
'label_attr' => [
'for' => 'nx_maximum_amount',
'help' => $translator->trans(
'Maximum order total in the default currency for which the multiple times payment method is available. Enter 0 for no maximum',
[],
Atos::MODULE_DOMAIN
)
]
]
)
;
}
/**
* @return string the name of you form. This name must be unique
*/
public function getName()
{
return 'atos_config';
}
}

View File

@@ -0,0 +1,93 @@
<?php
/*************************************************************************************/
/* */
/* Thelia */
/* */
/* Copyright (c) OpenStudio */
/* email : info@thelia.net */
/* web : http://www.thelia.net */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 3 of the License */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*************************************************************************************/
/**
* Created by Franck Allimant, CQFDev <franck@cqfdev.fr>
* Date: 11/01/2016 11:57
*/
namespace Atos\Hook;
use Atos\Atos;
use Thelia\Core\Event\Hook\HookRenderEvent;
use Thelia\Core\Hook\BaseHook;
use Thelia\Model\ModuleConfig;
use Thelia\Model\ModuleConfigQuery;
class HookManager extends BaseHook
{
const MAX_TRACE_SIZE_IN_BYTES = 40000;
public function onModuleConfigure(HookRenderEvent $event)
{
$logFilePath = sprintf(THELIA_ROOT."log".DS."%s.log", Atos::MODULE_DOMAIN);
if (false !== $fh = @fopen($logFilePath, "r")) {
if (filesize($logFilePath) > self::MAX_TRACE_SIZE_IN_BYTES) {
fseek($fh, -self::MAX_TRACE_SIZE_IN_BYTES, SEEK_END);
$truncated = true;
} else {
$truncated = false;
}
$traces = implode(
'<br>',
array_reverse(
explode(
"\n",
fread($fh, self::MAX_TRACE_SIZE_IN_BYTES)
)
)
);
fclose($fh);
if (empty($traces)) {
$traces = $this->translator->trans("The log file is currently empty.", [], Atos::MODULE_DOMAIN);
} elseif ($truncated) {
$traces = $this->translator->trans(
"(Previous log is in %file file.)<br>",
['%file' => sprintf("log" . DS . "%s.log", Atos::MODULE_DOMAIN)],
Atos::MODULE_DOMAIN
) . $traces;
}
} else {
$traces = $this->translator->trans(
"The log file '%log' does not exists yet.",
['%log' => $logFilePath],
Atos::MODULE_DOMAIN
);
}
$vars = [ 'trace_content' => $traces ];
if (null !== $params = ModuleConfigQuery::create()->findByModuleId(Atos::getModuleId())) {
/** @var ModuleConfig $param */
foreach ($params as $param) {
$vars[ $param->getName() ] = $param->getValue();
}
}
$event->add(
$this->render('atos/module-configuration.html', $vars)
);
}
}

View File

@@ -0,0 +1,13 @@
<?php
return array(
'Atos Configuration' => 'Atos Configuration',
'Atos-SIPS Platform configuration' => 'Atos-SIPS Platform configuration',
'Atos-SIPS call log to callback URL' => 'Atos-SIPS call log to callback URL',
'Callback URL' => 'Callback URL',
'Download full log' => 'Download full log',
'Operation mode' => 'Operation mode',
'Payment configuration' => 'Payment configuration',
'This is the callback URL, that will be called by Atos SIPS after customer payment. Be sure that this URL is reachable by remote mechines' => 'This is the callback URL, that will be called by Atos SIPS after customer payment. Be sure that this URL is reachable by remote machines',
'You can <a href="%url">edit the payment confirmation email</a> sent to the customer after a successful payment.' => 'You can <a href="%url">edit the payment confirmation email</a> sent to the customer after a successful payment.',
);

View File

@@ -0,0 +1,13 @@
<?php
return array(
'Atos Configuration' => 'Atos Configuration',
'Atos-SIPS Platform configuration' => 'Configuration de la plate-forme SIPS-Atos',
'Atos-SIPS call log to callback URL' => 'Atos-SIPS callback URL log',
'Callback URL' => 'URL de retour',
'Download full log' => 'Télécharger l\'historique complet',
'Operation mode' => 'Mode de fonctionnement',
'Payment configuration' => 'Configuration du paiement',
'This is the callback URL, that will be called by Atos SIPS after customer payment. Be sure that this URL is reachable by remote mechines' => 'Il s\'agit de l\'URL de retour appelée par Atos SIPS après le paiement de vos clients. Assurez-vous que cette URL est joignable par des machines distantes.',
'You can <a href="%url">edit the payment confirmation email</a> sent to the customer after a successful payment.' => 'Vous pouvez <a href="%url">modifier le mail de confirmation de paiement</a> envoyé au client.',
);

View File

@@ -0,0 +1,12 @@
<?php
return array(
'Dear customer' => 'Dear customer',
'Payment of your order %ref' => 'Payment of your order %ref',
'Thank you again for your purchase.' => 'Thank you again for your purchase.',
'The %store_name team.' => 'The %store_name team.',
'The payment of your order %ref with SIPS Atos is confirmed' => 'The payment of your order %ref with SIPS Atos is confirmed',
'This is a confirmation of the payment of your order %ref with SIPS-Atos on our shop.' => 'This is a confirmation of the payment of your order %ref with SIPS-Atos on our shop.',
'View this order in your account at %shop_name' => 'View this order in your account at %shop_name',
'Your invoice is now available in your customer account at %url.' => 'Your invoice is now available in your customer account at %url.',
);

View File

@@ -0,0 +1,12 @@
<?php
return array(
'Dear customer' => 'Cher client',
'Payment of your order %ref' => 'Paiement de votre commande %ref',
'Thank you again for your purchase.' => 'Merci encore pour votre commande.',
'The %store_name team.' => 'L\'équipe %store_name',
'The payment of your order %ref with SIPS Atos is confirmed' => 'Le paiement de votre commande %ref avec SIPS Atos est confirmé.',
'This is a confirmation of the payment of your order %ref with SIPS-Atos on our shop.' => 'Ce message confirme le paiement de votre commande %ref avec SIPS Atos dans notre boutique.',
'View this order in your account at %shop_name' => 'Les détails de cette commande sont disponibles dans votre compte client sur %shop_name',
'Your invoice is now available in your customer account at %url.' => 'Les détails de cette commande sont disponibles dans votre compte client sur %url',
);

View File

@@ -0,0 +1,44 @@
<?php
return array(
'(Previous log is in %file file.)\n' => '(Previous log is in %file file.)\n',
'ATOS certificate content' => 'ATOS certificate content',
'Allowed IPs in test mode' => 'Allowed IPs in test mode',
'Atos configuration' => 'Atos configuration',
'Atos payment module is nort properly configured. Please check module configuration in your back-office.' => 'Atos payment module is nort properly configured. Please check module configuration in your back-office.',
'Atos platform request processing terminated.' => 'Atos platform request processing terminated.',
'Atos-SIPS platform request received.' => 'Atos-SIPS platform request received.',
'Cannot find an order for transaction ID "%trans"' => 'Cannot find an order for transaction ID "%trans"',
'Cannot validate order. Response code is %resp' => 'Cannot validate order. Response code is %resp',
'Empty response recevied from Atos binary "%path". Please check path and permissions.' => 'Empty response recevied from Atos binary "%path". Please check path and permissions.',
'Error %code while processing response, with message %message' => 'Error %code while processing response, with message %message',
'Failed to read the %file file. Please check file and directory permissions.' => 'Failed to read the %file file. Please check file and directory permissions.',
'Failed to write certificate data in file \'%file\'. Please check file permission' => 'Failed to write certificate data in file \'%file\'. Please check file permission',
'File %file must be writable, please check Atos/Config directory permissions.' => 'File %file must be writable, please check Atos/Config directory permissions.',
'Got empty response from executable %binary, check path and permissions' => 'Got empty response from executable %binary, check path and permissions',
'If checked, a payment confirmation e-mail is sent to the customer.' => 'If checked, a payment confirmation e-mail is sent to the customer.',
'If checked, the order confirmation message is sent to the customer only when the payment is successful. The order notification is always sent to the shop administrator' => 'If checked, the order confirmation message is sent to the customer only when the payment is successful. The order notification is always sent to the shop administrator',
'List of IP addresses allowed to use this payment on the front-office when in test mode (your current IP is %ip). One address per line' => 'List of IP addresses allowed to use this payment on the front-office when in test mode (your current IP is %ip). One address per line',
'Maximum order total' => 'Maximum order total',
'Maximum order total in the default currency for which this payment method is available. Enter 0 for no maximum' => 'Maximum order total in the default currency for which this payment method is available. Enter 0 for no maximum',
'Minimum order total' => 'Minimum order total',
'Minimum order total in the default currency for which this payment method is available. Enter 0 for no minimum' => 'Minimum order total in the default currency for which this payment method is available. Enter 0 for no minimum',
'Operation Mode' => 'Operation Mode',
'Order ID %id is confirmed.' => 'Order ID %id is confirmed.',
'Please paste here the certificate downloaded from the Atos SIPS platform' => 'Please paste here the certificate downloaded from the Atos SIPS platform',
'Production' => 'Production',
'Request binary not found in "%path"' => 'Request binary not found in "%path"',
'Request does not contains any data' => 'Request does not contains any data',
'Response parameters : %resp' => 'Response parameters : %resp',
'Response request not found in %response' => 'Response request not found in %response',
'Send a payment confirmation e-mail' => 'Send a payment confirmation e-mail',
'Send order confirmation on payment success' => 'Send order confirmation on payment success',
'Shop Merchant ID' => 'Shop Merchant ID',
'Test' => 'Test',
'Test or production mode' => 'Test or production mode',
'The \'%file\' should be executable. Please check file permission' => 'The \'%file\' should be executable. Please check file permission',
'The log file \'%log\' does not exists yet.' => 'The log file \'%log\' does not exists yet.',
'The log file is currently empty.' => 'The log file is currently empty.',
'User canceled payment of order %id' => 'User canceled payment of order ID %id',
'you cancel the payment' => 'you cancel the payment',
);

View File

@@ -0,0 +1,44 @@
<?php
return array(
'(Previous log is in %file file.)\n' => '(L\'historique précédent se trouve dans n %file file.)\n',
'ATOS certificate content' => 'Certificat ATOS',
'Allowed IPs in test mode' => 'Adresse IP autorisées en phase de test',
'Atos configuration' => 'Configuration ATOS-SIPS',
'Atos payment module is nort properly configured. Please check module configuration in your back-office.' => 'Le module de paiement Atos n\'est pas correctement configuré. Merci de vérifier la configuration dans votre back-office.',
'Atos platform request processing terminated.' => 'Traitemenr de la requête ATOS terminé.',
'Atos-SIPS platform request received.' => 'Réception d\'une requête ATOS',
'Cannot find an order for transaction ID "%trans"' => 'Aucune commande ne correspond à l\'ID de transaction "%trans"',
'Cannot validate order. Response code is %resp' => 'La commande ne peut être validée. Le code de réponse est %resp',
'Empty response recevied from Atos binary "%path". Please check path and permissions.' => 'Le binaire Atos %path a retourné une réponse vide. Vérifiez que ce fichier est exécutable.',
'Error %code while processing response, with message %message' => 'Erreur %code lors du traitement de la réponse, avec le message %message',
'Failed to read the %file file. Please check file and directory permissions.' => 'Impossible de lire le fichier %file. Vérifier que le fichier existe et est est accessible en lecture.',
'Failed to write certificate data in file \'%file\'. Please check file permission' => 'L\'écriture des données du certificat dans le fichier %file a échoué. Vérifier que le fichier est accessible en écriture.',
'File %file must be writable, please check Atos/Config directory permissions.' => 'Le fichier %file doit être accessible en écriture, merci de vérifier qu\'il possède les permissions nécessaires.',
'Got empty response from executable %binary, check path and permissions' => 'L\'executable %binary a retourné une réponse vide. Vérifier les permissions du fichier.',
'If checked, a payment confirmation e-mail is sent to the customer.' => 'Si cette case est cochée, un mail de confirmation de paiement sera envoyé au client.',
'If checked, the order confirmation message is sent to the customer only when the payment is successful. The order notification is always sent to the shop administrator' => 'Si cette case est cochée, le mail de confirmation de commande sera envoyé au client seulement si son paiement est validé.',
'List of IP addresses allowed to use this payment on the front-office when in test mode (your current IP is %ip). One address per line' => 'Liste des adresse IP qui pourront choisir ce module de paiement en front-office pendant la phase de test (votre IP est %ip). Une adresse par ligne.',
'Maximum order total' => 'Montant de commande maximum',
'Maximum order total in the default currency for which this payment method is available. Enter 0 for no maximum' => 'Montant maximum dans la devise par défaut pour proposer ce moyen de paiement. Laisser 0 pour ne pas fixer de maximum.',
'Minimum order total' => 'Montant minimum de commande',
'Minimum order total in the default currency for which this payment method is available. Enter 0 for no minimum' => 'Montant minimum dans la devise par défaut pour proposer ce moyen de paiement. Laisser 0 pour ne pas fixer de minimum.',
'Operation Mode' => 'Mode de fonctionnement',
'Order ID %id is confirmed.' => 'La commande ID %id est confirmée',
'Please paste here the certificate downloaded from the Atos SIPS platform' => 'Vous pouvez coller ici le texte du certificat téléhergé depuis la plate-forme ATOS',
'Production' => 'Production',
'Request binary not found in "%path"' => 'le binaire request n\'est pas trouvé dans "%path"',
'Request does not contains any data' => 'La requête ne contient aucune donnée',
'Response parameters : %resp' => 'Paramètres de la réponse : %resp',
'Response request not found in %response' => 'La réponse ATOS n\'a pas été trouvée dans %response',
'Send a payment confirmation e-mail' => 'Envoyer une confirmation de paiement',
'Send order confirmation on payment success' => 'Confirmation de commande si le paiement réussit ',
'Shop Merchant ID' => 'Identifiant Marchand',
'Test' => 'Test',
'Test or production mode' => 'Test ou production',
'The \'%file\' should be executable. Please check file permission' => 'Le fichier %file doit être exécutable. Vérifier que ce fichier a les permission nécessaires/.',
'The log file \'%log\' does not exists yet.' => 'Le fichier de log %log n\'existe pas encore.',
'The log file is currently empty.' => 'Le fichier de log est vide',
'User canceled payment of order %id' => 'Le client a annulé le paiement de la commande ID %id',
'you cancel the payment' => 'Vous avez annulé le paiement',
);

View File

@@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

View File

@@ -0,0 +1,10 @@
<?php
namespace Atos\Model;
use Atos\Model\Base\AtosCurrency as BaseAtosCurrency;
class AtosCurrency extends BaseAtosCurrency
{
}

View File

@@ -0,0 +1,20 @@
<?php
namespace Atos\Model;
use Atos\Model\Base\AtosCurrencyQuery as BaseAtosCurrencyQuery;
/**
* Skeleton subclass for performing query and update operations on the 'atos_currency' table.
*
*
*
* You should add additional methods to this class to meet the
* application requirements. This class will only be generated as
* long as it does not already exist in the output directory.
*
*/
class AtosCurrencyQuery extends BaseAtosCurrencyQuery
{
} // AtosCurrencyQuery

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,419 @@
<?php
namespace Atos\Model\Base;
use \Exception;
use \PDO;
use Atos\Model\AtosCurrency as ChildAtosCurrency;
use Atos\Model\AtosCurrencyQuery as ChildAtosCurrencyQuery;
use Atos\Model\Map\AtosCurrencyTableMap;
use Propel\Runtime\Propel;
use Propel\Runtime\ActiveQuery\Criteria;
use Propel\Runtime\ActiveQuery\ModelCriteria;
use Propel\Runtime\Connection\ConnectionInterface;
use Propel\Runtime\Exception\PropelException;
/**
* Base class that represents a query for the 'atos_currency' table.
*
*
*
* @method ChildAtosCurrencyQuery orderByCode($order = Criteria::ASC) Order by the code column
* @method ChildAtosCurrencyQuery orderByAtosCode($order = Criteria::ASC) Order by the atos_code column
* @method ChildAtosCurrencyQuery orderByDecimals($order = Criteria::ASC) Order by the decimals column
*
* @method ChildAtosCurrencyQuery groupByCode() Group by the code column
* @method ChildAtosCurrencyQuery groupByAtosCode() Group by the atos_code column
* @method ChildAtosCurrencyQuery groupByDecimals() Group by the decimals column
*
* @method ChildAtosCurrencyQuery leftJoin($relation) Adds a LEFT JOIN clause to the query
* @method ChildAtosCurrencyQuery rightJoin($relation) Adds a RIGHT JOIN clause to the query
* @method ChildAtosCurrencyQuery innerJoin($relation) Adds a INNER JOIN clause to the query
*
* @method ChildAtosCurrency findOne(ConnectionInterface $con = null) Return the first ChildAtosCurrency matching the query
* @method ChildAtosCurrency findOneOrCreate(ConnectionInterface $con = null) Return the first ChildAtosCurrency matching the query, or a new ChildAtosCurrency object populated from the query conditions when no match is found
*
* @method ChildAtosCurrency findOneByCode(string $code) Return the first ChildAtosCurrency filtered by the code column
* @method ChildAtosCurrency findOneByAtosCode(int $atos_code) Return the first ChildAtosCurrency filtered by the atos_code column
* @method ChildAtosCurrency findOneByDecimals(int $decimals) Return the first ChildAtosCurrency filtered by the decimals column
*
* @method array findByCode(string $code) Return ChildAtosCurrency objects filtered by the code column
* @method array findByAtosCode(int $atos_code) Return ChildAtosCurrency objects filtered by the atos_code column
* @method array findByDecimals(int $decimals) Return ChildAtosCurrency objects filtered by the decimals column
*
*/
abstract class AtosCurrencyQuery extends ModelCriteria
{
/**
* Initializes internal state of \Atos\Model\Base\AtosCurrencyQuery object.
*
* @param string $dbName The database name
* @param string $modelName The phpName of a model, e.g. 'Book'
* @param string $modelAlias The alias for the model in this query, e.g. 'b'
*/
public function __construct($dbName = 'thelia', $modelName = '\\Atos\\Model\\AtosCurrency', $modelAlias = null)
{
parent::__construct($dbName, $modelName, $modelAlias);
}
/**
* Returns a new ChildAtosCurrencyQuery object.
*
* @param string $modelAlias The alias of a model in the query
* @param Criteria $criteria Optional Criteria to build the query from
*
* @return ChildAtosCurrencyQuery
*/
public static function create($modelAlias = null, $criteria = null)
{
if ($criteria instanceof \Atos\Model\AtosCurrencyQuery) {
return $criteria;
}
$query = new \Atos\Model\AtosCurrencyQuery();
if (null !== $modelAlias) {
$query->setModelAlias($modelAlias);
}
if ($criteria instanceof Criteria) {
$query->mergeWith($criteria);
}
return $query;
}
/**
* Find object by primary key.
* Propel uses the instance pool to skip the database if the object exists.
* Go fast if the query is untouched.
*
* <code>
* $obj = $c->findPk(12, $con);
* </code>
*
* @param mixed $key Primary key to use for the query
* @param ConnectionInterface $con an optional connection object
*
* @return ChildAtosCurrency|array|mixed the result, formatted by the current formatter
*/
public function findPk($key, $con = null)
{
if ($key === null) {
return null;
}
if ((null !== ($obj = AtosCurrencyTableMap::getInstanceFromPool((string) $key))) && !$this->formatter) {
// the object is already in the instance pool
return $obj;
}
if ($con === null) {
$con = Propel::getServiceContainer()->getReadConnection(AtosCurrencyTableMap::DATABASE_NAME);
}
$this->basePreSelect($con);
if ($this->formatter || $this->modelAlias || $this->with || $this->select
|| $this->selectColumns || $this->asColumns || $this->selectModifiers
|| $this->map || $this->having || $this->joins) {
return $this->findPkComplex($key, $con);
} else {
return $this->findPkSimple($key, $con);
}
}
/**
* Find object by primary key using raw SQL to go fast.
* Bypass doSelect() and the object formatter by using generated code.
*
* @param mixed $key Primary key to use for the query
* @param ConnectionInterface $con A connection object
*
* @return ChildAtosCurrency A model object, or null if the key is not found
*/
protected function findPkSimple($key, $con)
{
$sql = 'SELECT CODE, ATOS_CODE, DECIMALS FROM atos_currency WHERE CODE = :p0';
try {
$stmt = $con->prepare($sql);
$stmt->bindValue(':p0', $key, PDO::PARAM_STR);
$stmt->execute();
} catch (Exception $e) {
Propel::log($e->getMessage(), Propel::LOG_ERR);
throw new PropelException(sprintf('Unable to execute SELECT statement [%s]', $sql), 0, $e);
}
$obj = null;
if ($row = $stmt->fetch(\PDO::FETCH_NUM)) {
$obj = new ChildAtosCurrency();
$obj->hydrate($row);
AtosCurrencyTableMap::addInstanceToPool($obj, (string) $key);
}
$stmt->closeCursor();
return $obj;
}
/**
* Find object by primary key.
*
* @param mixed $key Primary key to use for the query
* @param ConnectionInterface $con A connection object
*
* @return ChildAtosCurrency|array|mixed the result, formatted by the current formatter
*/
protected function findPkComplex($key, $con)
{
// As the query uses a PK condition, no limit(1) is necessary.
$criteria = $this->isKeepQuery() ? clone $this : $this;
$dataFetcher = $criteria
->filterByPrimaryKey($key)
->doSelect($con);
return $criteria->getFormatter()->init($criteria)->formatOne($dataFetcher);
}
/**
* Find objects by primary key
* <code>
* $objs = $c->findPks(array(12, 56, 832), $con);
* </code>
* @param array $keys Primary keys to use for the query
* @param ConnectionInterface $con an optional connection object
*
* @return ObjectCollection|array|mixed the list of results, formatted by the current formatter
*/
public function findPks($keys, $con = null)
{
if (null === $con) {
$con = Propel::getServiceContainer()->getReadConnection($this->getDbName());
}
$this->basePreSelect($con);
$criteria = $this->isKeepQuery() ? clone $this : $this;
$dataFetcher = $criteria
->filterByPrimaryKeys($keys)
->doSelect($con);
return $criteria->getFormatter()->init($criteria)->format($dataFetcher);
}
/**
* Filter the query by primary key
*
* @param mixed $key Primary key to use for the query
*
* @return ChildAtosCurrencyQuery The current query, for fluid interface
*/
public function filterByPrimaryKey($key)
{
return $this->addUsingAlias(AtosCurrencyTableMap::CODE, $key, Criteria::EQUAL);
}
/**
* Filter the query by a list of primary keys
*
* @param array $keys The list of primary key to use for the query
*
* @return ChildAtosCurrencyQuery The current query, for fluid interface
*/
public function filterByPrimaryKeys($keys)
{
return $this->addUsingAlias(AtosCurrencyTableMap::CODE, $keys, Criteria::IN);
}
/**
* Filter the query on the code column
*
* Example usage:
* <code>
* $query->filterByCode('fooValue'); // WHERE code = 'fooValue'
* $query->filterByCode('%fooValue%'); // WHERE code LIKE '%fooValue%'
* </code>
*
* @param string $code The value to use as filter.
* Accepts wildcards (* and % trigger a LIKE)
* @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
*
* @return ChildAtosCurrencyQuery The current query, for fluid interface
*/
public function filterByCode($code = null, $comparison = null)
{
if (null === $comparison) {
if (is_array($code)) {
$comparison = Criteria::IN;
} elseif (preg_match('/[\%\*]/', $code)) {
$code = str_replace('*', '%', $code);
$comparison = Criteria::LIKE;
}
}
return $this->addUsingAlias(AtosCurrencyTableMap::CODE, $code, $comparison);
}
/**
* Filter the query on the atos_code column
*
* Example usage:
* <code>
* $query->filterByAtosCode(1234); // WHERE atos_code = 1234
* $query->filterByAtosCode(array(12, 34)); // WHERE atos_code IN (12, 34)
* $query->filterByAtosCode(array('min' => 12)); // WHERE atos_code > 12
* </code>
*
* @param mixed $atosCode The value to use as filter.
* Use scalar values for equality.
* Use array values for in_array() equivalent.
* Use associative array('min' => $minValue, 'max' => $maxValue) for intervals.
* @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
*
* @return ChildAtosCurrencyQuery The current query, for fluid interface
*/
public function filterByAtosCode($atosCode = null, $comparison = null)
{
if (is_array($atosCode)) {
$useMinMax = false;
if (isset($atosCode['min'])) {
$this->addUsingAlias(AtosCurrencyTableMap::ATOS_CODE, $atosCode['min'], Criteria::GREATER_EQUAL);
$useMinMax = true;
}
if (isset($atosCode['max'])) {
$this->addUsingAlias(AtosCurrencyTableMap::ATOS_CODE, $atosCode['max'], Criteria::LESS_EQUAL);
$useMinMax = true;
}
if ($useMinMax) {
return $this;
}
if (null === $comparison) {
$comparison = Criteria::IN;
}
}
return $this->addUsingAlias(AtosCurrencyTableMap::ATOS_CODE, $atosCode, $comparison);
}
/**
* Filter the query on the decimals column
*
* Example usage:
* <code>
* $query->filterByDecimals(1234); // WHERE decimals = 1234
* $query->filterByDecimals(array(12, 34)); // WHERE decimals IN (12, 34)
* $query->filterByDecimals(array('min' => 12)); // WHERE decimals > 12
* </code>
*
* @param mixed $decimals The value to use as filter.
* Use scalar values for equality.
* Use array values for in_array() equivalent.
* Use associative array('min' => $minValue, 'max' => $maxValue) for intervals.
* @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
*
* @return ChildAtosCurrencyQuery The current query, for fluid interface
*/
public function filterByDecimals($decimals = null, $comparison = null)
{
if (is_array($decimals)) {
$useMinMax = false;
if (isset($decimals['min'])) {
$this->addUsingAlias(AtosCurrencyTableMap::DECIMALS, $decimals['min'], Criteria::GREATER_EQUAL);
$useMinMax = true;
}
if (isset($decimals['max'])) {
$this->addUsingAlias(AtosCurrencyTableMap::DECIMALS, $decimals['max'], Criteria::LESS_EQUAL);
$useMinMax = true;
}
if ($useMinMax) {
return $this;
}
if (null === $comparison) {
$comparison = Criteria::IN;
}
}
return $this->addUsingAlias(AtosCurrencyTableMap::DECIMALS, $decimals, $comparison);
}
/**
* Exclude object from result
*
* @param ChildAtosCurrency $atosCurrency Object to remove from the list of results
*
* @return ChildAtosCurrencyQuery The current query, for fluid interface
*/
public function prune($atosCurrency = null)
{
if ($atosCurrency) {
$this->addUsingAlias(AtosCurrencyTableMap::CODE, $atosCurrency->getCode(), Criteria::NOT_EQUAL);
}
return $this;
}
/**
* Deletes all rows from the atos_currency table.
*
* @param ConnectionInterface $con the connection to use
* @return int The number of affected rows (if supported by underlying database driver).
*/
public function doDeleteAll(ConnectionInterface $con = null)
{
if (null === $con) {
$con = Propel::getServiceContainer()->getWriteConnection(AtosCurrencyTableMap::DATABASE_NAME);
}
$affectedRows = 0; // initialize var to track total num of affected rows
try {
// use transaction because $criteria could contain info
// for more than one table or we could emulating ON DELETE CASCADE, etc.
$con->beginTransaction();
$affectedRows += parent::doDeleteAll($con);
// Because this db requires some delete cascade/set null emulation, we have to
// clear the cached instance *after* the emulation has happened (since
// instances get re-added by the select statement contained therein).
AtosCurrencyTableMap::clearInstancePool();
AtosCurrencyTableMap::clearRelatedInstancePool();
$con->commit();
} catch (PropelException $e) {
$con->rollBack();
throw $e;
}
return $affectedRows;
}
/**
* Performs a DELETE on the database, given a ChildAtosCurrency or Criteria object OR a primary key value.
*
* @param mixed $values Criteria or ChildAtosCurrency object or primary key or array of primary keys
* which is used to create the DELETE statement
* @param ConnectionInterface $con the connection to use
* @return int The number of affected rows (if supported by underlying database driver). This includes CASCADE-related rows
* if supported by native driver or if emulated using Propel.
* @throws PropelException Any exceptions caught during processing will be
* rethrown wrapped into a PropelException.
*/
public function delete(ConnectionInterface $con = null)
{
if (null === $con) {
$con = Propel::getServiceContainer()->getWriteConnection(AtosCurrencyTableMap::DATABASE_NAME);
}
$criteria = $this;
// Set the correct dbName
$criteria->setDbName(AtosCurrencyTableMap::DATABASE_NAME);
$affectedRows = 0; // initialize var to track total num of affected rows
try {
// use transaction because $criteria could contain info
// for more than one table or we could emulating ON DELETE CASCADE, etc.
$con->beginTransaction();
AtosCurrencyTableMap::removeInstanceFromPool($criteria);
$affectedRows += ModelCriteria::delete($con);
AtosCurrencyTableMap::clearRelatedInstancePool();
$con->commit();
return $affectedRows;
} catch (PropelException $e) {
$con->rollBack();
throw $e;
}
}
} // AtosCurrencyQuery

View File

@@ -0,0 +1,411 @@
<?php
namespace Atos\Model\Map;
use Atos\Model\AtosCurrency;
use Atos\Model\AtosCurrencyQuery;
use Propel\Runtime\Propel;
use Propel\Runtime\ActiveQuery\Criteria;
use Propel\Runtime\ActiveQuery\InstancePoolTrait;
use Propel\Runtime\Connection\ConnectionInterface;
use Propel\Runtime\DataFetcher\DataFetcherInterface;
use Propel\Runtime\Exception\PropelException;
use Propel\Runtime\Map\RelationMap;
use Propel\Runtime\Map\TableMap;
use Propel\Runtime\Map\TableMapTrait;
/**
* This class defines the structure of the 'atos_currency' table.
*
*
*
* This map class is used by Propel to do runtime db structure discovery.
* For example, the createSelectSql() method checks the type of a given column used in an
* ORDER BY clause to know whether it needs to apply SQL to make the ORDER BY case-insensitive
* (i.e. if it's a text column type).
*
*/
class AtosCurrencyTableMap extends TableMap
{
use InstancePoolTrait;
use TableMapTrait;
/**
* The (dot-path) name of this class
*/
const CLASS_NAME = 'Atos.Model.Map.AtosCurrencyTableMap';
/**
* The default database name for this class
*/
const DATABASE_NAME = 'thelia';
/**
* The table name for this class
*/
const TABLE_NAME = 'atos_currency';
/**
* The related Propel class for this table
*/
const OM_CLASS = '\\Atos\\Model\\AtosCurrency';
/**
* A class that can be returned by this tableMap
*/
const CLASS_DEFAULT = 'Atos.Model.AtosCurrency';
/**
* The total number of columns
*/
const NUM_COLUMNS = 3;
/**
* The number of lazy-loaded columns
*/
const NUM_LAZY_LOAD_COLUMNS = 0;
/**
* The number of columns to hydrate (NUM_COLUMNS - NUM_LAZY_LOAD_COLUMNS)
*/
const NUM_HYDRATE_COLUMNS = 3;
/**
* the column name for the CODE field
*/
const CODE = 'atos_currency.CODE';
/**
* the column name for the ATOS_CODE field
*/
const ATOS_CODE = 'atos_currency.ATOS_CODE';
/**
* the column name for the DECIMALS field
*/
const DECIMALS = 'atos_currency.DECIMALS';
/**
* The default string format for model objects of the related table
*/
const DEFAULT_STRING_FORMAT = 'YAML';
/**
* holds an array of fieldnames
*
* first dimension keys are the type constants
* e.g. self::$fieldNames[self::TYPE_PHPNAME][0] = 'Id'
*/
protected static $fieldNames = array (
self::TYPE_PHPNAME => array('Code', 'AtosCode', 'Decimals', ),
self::TYPE_STUDLYPHPNAME => array('code', 'atosCode', 'decimals', ),
self::TYPE_COLNAME => array(AtosCurrencyTableMap::CODE, AtosCurrencyTableMap::ATOS_CODE, AtosCurrencyTableMap::DECIMALS, ),
self::TYPE_RAW_COLNAME => array('CODE', 'ATOS_CODE', 'DECIMALS', ),
self::TYPE_FIELDNAME => array('code', 'atos_code', 'decimals', ),
self::TYPE_NUM => array(0, 1, 2, )
);
/**
* holds an array of keys for quick access to the fieldnames array
*
* first dimension keys are the type constants
* e.g. self::$fieldKeys[self::TYPE_PHPNAME]['Id'] = 0
*/
protected static $fieldKeys = array (
self::TYPE_PHPNAME => array('Code' => 0, 'AtosCode' => 1, 'Decimals' => 2, ),
self::TYPE_STUDLYPHPNAME => array('code' => 0, 'atosCode' => 1, 'decimals' => 2, ),
self::TYPE_COLNAME => array(AtosCurrencyTableMap::CODE => 0, AtosCurrencyTableMap::ATOS_CODE => 1, AtosCurrencyTableMap::DECIMALS => 2, ),
self::TYPE_RAW_COLNAME => array('CODE' => 0, 'ATOS_CODE' => 1, 'DECIMALS' => 2, ),
self::TYPE_FIELDNAME => array('code' => 0, 'atos_code' => 1, 'decimals' => 2, ),
self::TYPE_NUM => array(0, 1, 2, )
);
/**
* Initialize the table attributes and columns
* Relations are not initialized by this method since they are lazy loaded
*
* @return void
* @throws PropelException
*/
public function initialize()
{
// attributes
$this->setName('atos_currency');
$this->setPhpName('AtosCurrency');
$this->setClassName('\\Atos\\Model\\AtosCurrency');
$this->setPackage('Atos.Model');
$this->setUseIdGenerator(false);
// columns
$this->addPrimaryKey('CODE', 'Code', 'VARCHAR', true, 128, null);
$this->addColumn('ATOS_CODE', 'AtosCode', 'INTEGER', false, null, null);
$this->addColumn('DECIMALS', 'Decimals', 'INTEGER', false, null, null);
} // initialize()
/**
* Build the RelationMap objects for this table relationships
*/
public function buildRelations()
{
} // buildRelations()
/**
* Retrieves a string version of the primary key from the DB resultset row that can be used to uniquely identify a row in this table.
*
* For tables with a single-column primary key, that simple pkey value will be returned. For tables with
* a multi-column primary key, a serialize()d version of the primary key will be returned.
*
* @param array $row resultset row.
* @param int $offset The 0-based offset for reading from the resultset row.
* @param string $indexType One of the class type constants TableMap::TYPE_PHPNAME, TableMap::TYPE_STUDLYPHPNAME
* TableMap::TYPE_COLNAME, TableMap::TYPE_FIELDNAME, TableMap::TYPE_NUM
*/
public static function getPrimaryKeyHashFromRow($row, $offset = 0, $indexType = TableMap::TYPE_NUM)
{
// If the PK cannot be derived from the row, return NULL.
if ($row[TableMap::TYPE_NUM == $indexType ? 0 + $offset : static::translateFieldName('Code', TableMap::TYPE_PHPNAME, $indexType)] === null) {
return null;
}
return (string) $row[TableMap::TYPE_NUM == $indexType ? 0 + $offset : static::translateFieldName('Code', TableMap::TYPE_PHPNAME, $indexType)];
}
/**
* Retrieves the primary key from the DB resultset row
* For tables with a single-column primary key, that simple pkey value will be returned. For tables with
* a multi-column primary key, an array of the primary key columns will be returned.
*
* @param array $row resultset row.
* @param int $offset The 0-based offset for reading from the resultset row.
* @param string $indexType One of the class type constants TableMap::TYPE_PHPNAME, TableMap::TYPE_STUDLYPHPNAME
* TableMap::TYPE_COLNAME, TableMap::TYPE_FIELDNAME, TableMap::TYPE_NUM
*
* @return mixed The primary key of the row
*/
public static function getPrimaryKeyFromRow($row, $offset = 0, $indexType = TableMap::TYPE_NUM)
{
return (string) $row[
$indexType == TableMap::TYPE_NUM
? 0 + $offset
: self::translateFieldName('Code', TableMap::TYPE_PHPNAME, $indexType)
];
}
/**
* The class that the tableMap will make instances of.
*
* If $withPrefix is true, the returned path
* uses a dot-path notation which is translated into a path
* relative to a location on the PHP include_path.
* (e.g. path.to.MyClass -> 'path/to/MyClass.php')
*
* @param boolean $withPrefix Whether or not to return the path with the class name
* @return string path.to.ClassName
*/
public static function getOMClass($withPrefix = true)
{
return $withPrefix ? AtosCurrencyTableMap::CLASS_DEFAULT : AtosCurrencyTableMap::OM_CLASS;
}
/**
* Populates an object of the default type or an object that inherit from the default.
*
* @param array $row row returned by DataFetcher->fetch().
* @param int $offset The 0-based offset for reading from the resultset row.
* @param string $indexType The index type of $row. Mostly DataFetcher->getIndexType().
One of the class type constants TableMap::TYPE_PHPNAME, TableMap::TYPE_STUDLYPHPNAME
* TableMap::TYPE_COLNAME, TableMap::TYPE_FIELDNAME, TableMap::TYPE_NUM.
*
* @throws PropelException Any exceptions caught during processing will be
* rethrown wrapped into a PropelException.
* @return array (AtosCurrency object, last column rank)
*/
public static function populateObject($row, $offset = 0, $indexType = TableMap::TYPE_NUM)
{
$key = AtosCurrencyTableMap::getPrimaryKeyHashFromRow($row, $offset, $indexType);
if (null !== ($obj = AtosCurrencyTableMap::getInstanceFromPool($key))) {
// We no longer rehydrate the object, since this can cause data loss.
// See http://www.propelorm.org/ticket/509
// $obj->hydrate($row, $offset, true); // rehydrate
$col = $offset + AtosCurrencyTableMap::NUM_HYDRATE_COLUMNS;
} else {
$cls = AtosCurrencyTableMap::OM_CLASS;
$obj = new $cls();
$col = $obj->hydrate($row, $offset, false, $indexType);
AtosCurrencyTableMap::addInstanceToPool($obj, $key);
}
return array($obj, $col);
}
/**
* The returned array will contain objects of the default type or
* objects that inherit from the default.
*
* @param DataFetcherInterface $dataFetcher
* @return array
* @throws PropelException Any exceptions caught during processing will be
* rethrown wrapped into a PropelException.
*/
public static function populateObjects(DataFetcherInterface $dataFetcher)
{
$results = array();
// set the class once to avoid overhead in the loop
$cls = static::getOMClass(false);
// populate the object(s)
while ($row = $dataFetcher->fetch()) {
$key = AtosCurrencyTableMap::getPrimaryKeyHashFromRow($row, 0, $dataFetcher->getIndexType());
if (null !== ($obj = AtosCurrencyTableMap::getInstanceFromPool($key))) {
// We no longer rehydrate the object, since this can cause data loss.
// See http://www.propelorm.org/ticket/509
// $obj->hydrate($row, 0, true); // rehydrate
$results[] = $obj;
} else {
$obj = new $cls();
$obj->hydrate($row);
$results[] = $obj;
AtosCurrencyTableMap::addInstanceToPool($obj, $key);
} // if key exists
}
return $results;
}
/**
* Add all the columns needed to create a new object.
*
* Note: any columns that were marked with lazyLoad="true" in the
* XML schema will not be added to the select list and only loaded
* on demand.
*
* @param Criteria $criteria object containing the columns to add.
* @param string $alias optional table alias
* @throws PropelException Any exceptions caught during processing will be
* rethrown wrapped into a PropelException.
*/
public static function addSelectColumns(Criteria $criteria, $alias = null)
{
if (null === $alias) {
$criteria->addSelectColumn(AtosCurrencyTableMap::CODE);
$criteria->addSelectColumn(AtosCurrencyTableMap::ATOS_CODE);
$criteria->addSelectColumn(AtosCurrencyTableMap::DECIMALS);
} else {
$criteria->addSelectColumn($alias . '.CODE');
$criteria->addSelectColumn($alias . '.ATOS_CODE');
$criteria->addSelectColumn($alias . '.DECIMALS');
}
}
/**
* Returns the TableMap related to this object.
* This method is not needed for general use but a specific application could have a need.
* @return TableMap
* @throws PropelException Any exceptions caught during processing will be
* rethrown wrapped into a PropelException.
*/
public static function getTableMap()
{
return Propel::getServiceContainer()->getDatabaseMap(AtosCurrencyTableMap::DATABASE_NAME)->getTable(AtosCurrencyTableMap::TABLE_NAME);
}
/**
* Add a TableMap instance to the database for this tableMap class.
*/
public static function buildTableMap()
{
$dbMap = Propel::getServiceContainer()->getDatabaseMap(AtosCurrencyTableMap::DATABASE_NAME);
if (!$dbMap->hasTable(AtosCurrencyTableMap::TABLE_NAME)) {
$dbMap->addTableObject(new AtosCurrencyTableMap());
}
}
/**
* Performs a DELETE on the database, given a AtosCurrency or Criteria object OR a primary key value.
*
* @param mixed $values Criteria or AtosCurrency object or primary key or array of primary keys
* which is used to create the DELETE statement
* @param ConnectionInterface $con the connection to use
* @return int The number of affected rows (if supported by underlying database driver). This includes CASCADE-related rows
* if supported by native driver or if emulated using Propel.
* @throws PropelException Any exceptions caught during processing will be
* rethrown wrapped into a PropelException.
*/
public static function doDelete($values, ConnectionInterface $con = null)
{
if (null === $con) {
$con = Propel::getServiceContainer()->getWriteConnection(AtosCurrencyTableMap::DATABASE_NAME);
}
if ($values instanceof Criteria) {
// rename for clarity
$criteria = $values;
} elseif ($values instanceof \Atos\Model\AtosCurrency) { // it's a model object
// create criteria based on pk values
$criteria = $values->buildPkeyCriteria();
} else { // it's a primary key, or an array of pks
$criteria = new Criteria(AtosCurrencyTableMap::DATABASE_NAME);
$criteria->add(AtosCurrencyTableMap::CODE, (array) $values, Criteria::IN);
}
$query = AtosCurrencyQuery::create()->mergeWith($criteria);
if ($values instanceof Criteria) { AtosCurrencyTableMap::clearInstancePool();
} elseif (!is_object($values)) { // it's a primary key, or an array of pks
foreach ((array) $values as $singleval) { AtosCurrencyTableMap::removeInstanceFromPool($singleval);
}
}
return $query->delete($con);
}
/**
* Deletes all rows from the atos_currency table.
*
* @param ConnectionInterface $con the connection to use
* @return int The number of affected rows (if supported by underlying database driver).
*/
public static function doDeleteAll(ConnectionInterface $con = null)
{
return AtosCurrencyQuery::create()->doDeleteAll($con);
}
/**
* Performs an INSERT on the database, given a AtosCurrency or Criteria object.
*
* @param mixed $criteria Criteria or AtosCurrency object containing data that is used to create the INSERT statement.
* @param ConnectionInterface $con the ConnectionInterface connection to use
* @return mixed The new primary key.
* @throws PropelException Any exceptions caught during processing will be
* rethrown wrapped into a PropelException.
*/
public static function doInsert($criteria, ConnectionInterface $con = null)
{
if (null === $con) {
$con = Propel::getServiceContainer()->getWriteConnection(AtosCurrencyTableMap::DATABASE_NAME);
}
if ($criteria instanceof Criteria) {
$criteria = clone $criteria; // rename for clarity
} else {
$criteria = $criteria->buildCriteria(); // build Criteria from AtosCurrency object
}
// Set the correct dbName
$query = AtosCurrencyQuery::create()->mergeWith($criteria);
try {
// use transaction because $criteria could contain info
// for more than one table (I guess, conceivably)
$con->beginTransaction();
$pk = $query->doInsert($con);
$con->commit();
} catch (PropelException $e) {
$con->rollBack();
throw $e;
}
return $pk;
}
} // AtosCurrencyTableMap
// This is the static code needed to register the TableMap for this table with the main Propel class.
//
AtosCurrencyTableMap::buildTableMap();

View File

@@ -0,0 +1,71 @@
# Atos-SIPS Payment Module
------------------------
## English instructions
This module offers to your customers the Atos SIPS payment system, which is widely used by the french banks under different names: Mercanet, E-Transactions, Citelis, and much more.
### Installation
#### Manually
Install the Atos module using the Module page of your back office to upload the archive.
You can also extract the archive in the `<thelia root>/local/modules` directory. Be sure that the name of the module's directory is `Atos` (and not `Atos-master`, for example).
Activate the module from the Modules page of your back-office.
#### composer
```
$ composer require thelia/atos-module:~1.0
```
### Usage
You have to configure the Atos module before starting to use it. To do so, go to the "Modules" tab of your Thelia back-office, and activate the Atos module.
Then click the "Configure" button, and enter the required information. In most case, you'll receive your merchant ID by e-mail, and you'll receive instructions to download your certificate.
The module performs several checks when the configuration is saved, especially the execution permissions on the Atos binaries.
During the test phase, you can define the IP addresses allowed to use the Atos module on the front office, so that your customers will not be able to pay with Atos during this test phase.
A log of Atos post-payment callbacks is displayed in the configuration page.
### Payment template
You can customize the payment page ```templates/atos/payment.html``` to provide a better integration in your template, but the payment form data cannot be modified, as it is generated and signed by the Atos binary.
## Instructions en français
Ce module permet à vos clients de payer leurs commande par carte bancaire via la plate-forme Atos SIPS, utilisée par de nombreuses banques françaises sous diverses dénominations commerciales: Mercanet, Citelis, E-Transactions, et bien d'autres.
## Installation
### Manuellement
Installez ce module directement depuis la page Modules de votre back-office, en envoyant le fichier zip du module.
Vous pouvez aussi décompresser le module, et le placer manuellement dans le dossier ```<thelia_root>/local/modules```. Assurez-vous que le nom du dossier est bien ```Atos```, et pas ```Atos-master```
### composer
```
$ composer require thelia/atos-module:~1.0
```
## Utilisation
Pour utiliser le module Atos, vous devez tout d'abord le configurer. Pour ce faire, rendez-vous dans votre back-office, onglet Modules, et activez le module Atos.
Cliquez ensuite sur "Configurer" sur la ligne du module, et renseignez les informations requises. Dans la plupart des cas, l'ID Marchand vous a été communiqué par votre banque par e-mail, et vous devez recevoir les instructions qui vous permettront de télécharger le certificat.
Le module réalise plusieurs vérifications de votre configuration, et vous signalera les problèmes éventuellement rencontrés. Il contrôle notamment que les exécutables Atos possèdent bien les permissions d'exécution.
Lors de la phase de test, vous pouvez définir les adresses IP qui seront autorisées à utiliser le module en front-office, afin de ne pas laisser vos clients payer leur commandes avec Atos pendant cette phase.
## Template de paiement
Vous pouvez adapter la page de paiement qui se trouve dans ```templates/atos/payment.html```, et l'adapter à votre template, mais la form de paiement en elle-même ne peut pas être modifiée, elle est générée et signée par le binaire Atos, et ne doit pas être modifiée.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,11 @@
{
"name": "thelia/atos-module",
"license": "GPL-3.0+",
"type": "thelia-module",
"require": {
"thelia/installer": "~1.1"
},
"extra": {
"installer-name": "Atos"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 682 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 956 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,120 @@
<div class="row">
<div class="col-md-12 general-block-decorator">
<div class="row">
<div class="col-md-12 title title-without-tabs">
{intl d='atos.bo.default' l="Atos Configuration"}
</div>
</div>
<div class="form-container">
<div class="row">
<div class="col-md-12">
{form name="atos_configuration"}
<form action="{url path="/admin/module/atos/configure"}" method="post">
{form_hidden_fields form=$form}
{include file = "includes/inner-form-toolbar.html"
hide_flags = true
page_url = "{url path='/admin/module/Atos'}"
close_url = "{url path='/admin/modules'}"
}
{if $form_error}
<div class="row">
<div class="col-md-12">
<div class="alert alert-danger">{$form_error_message}</div>
</div>
</div>
{/if}
<div class="row">
<div class="col-md-4">
<p class="title title-without-tabs">{intl d='atos.bo.default' l="Atos-SIPS Platform configuration"}</p>
{render_form_field form=$form field="atos_merchantId" value=$atos_merchantId}
{render_form_field form=$form field="atos_certificate" value=$atos_certificate}
<div class="form-group">
<label class="control-label">
{intl d='atos.bo.default' l="Callback URL"}
</label>
<div class="well well-sm">{url path='/atos/callback'}</div>
<span class="help-block">
{intl d='atos.bo.default' l="This is the callback URL, that will be called by Atos SIPS after customer payment. Be sure that this URL is reachable by remote mechines"}
</span>
</div>
</div>
<div class="col-md-4">
<p class="title title-without-tabs">{intl d='atos.bo.default' l="Operation mode"}</p>
{render_form_field form=$form field="atos_mode" value=$atos_mode}
{render_form_field form=$form field="atos_allowed_ip_list" value=$atos_allowed_ip_list}
<p class="title title-without-tabs">{intl d='atos.bo.default' l="Payment by N installment"}</p>
{loop name="multi-plugin-enabled" type="module" code="AtosNx" active="1"}{/loop}
{elseloop rel="multi-plugin-enabled"}
<div class="alert alert-info">
{intl l="Install and activate Atos multiple times payment module (AtoxNx) to get configuration options." d='payzen.ai'}
</div>
{/elseloop}
{render_form_field form=$form field="nx_nb_installments" value=$nx_nb_installments|default:3}
{render_form_field form=$form field="nx_minimum_amount" value=$nx_minimum_amount|default:0}
{render_form_field form=$form field="nx_maximum_amount" value=$nx_maximum_amount|default:0}
</div>
<div class="col-md-4">
<p class="title title-without-tabs">{intl d='atos.bo.default' l="Payment configuration"}</p>
{custom_render_form_field form=$form field="send_confirmation_message_only_if_paid"}
<input type="checkbox" {form_field_attributes form=$form field="send_confirmation_message_only_if_paid"} {if $send_confirmation_message_only_if_paid}checked{/if}>
{$label}
{/custom_render_form_field}
{custom_render_form_field form=$form field="send_payment_confirmation_message"}
<input type="checkbox" {form_field_attributes form=$form field="send_payment_confirmation_message"} {if $send_payment_confirmation_message}checked{/if}>
{$label}
{/custom_render_form_field}
<div class="well well-sm">
<span class="glyphicon glyphicon-info-sign"></span>
{intl d='paypal.bo.default' l='You can <a href="%url">edit the payment confirmation email</a> sent to the customer after a successful payment.' url={url path="/admin/configuration/messages"}}
</div>
{render_form_field form=$form field="atos_minimum_amount" value=$atos_minimum_amount}
{render_form_field form=$form field="atos_maximum_amount" value=$atos_maximum_amount}
</div>
</div>
</form>
{/form}
<div class="row">
<div class="col-xs-12">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">
<span class="glyphicon glyphicon-cog"></span>
{intl d='atos.bo.default' l="Atos-SIPS call log to callback URL"}
</h3>
</div>
<div class="panel-body">
<div id="log-container" style="font-family: monospace; font-size: 12px; max-height: 400px; overflow-y: scroll">
{$trace_content nofilter}
</div>
</div>
<div class="panel-footer">
<a href="{url path='/admin/module/atos/log'}" class="btn btn-sm btn-primary">{intl d='atos.bo.default' l="Download full log"}</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,23 @@
{extends file="email-layout.tpl"}
{* Do not provide a "Open in browser" link *}
{block name="browser"}{/block}
{* No pre-header *}
{block name="pre-header"}{/block}
{* Subject *}
{block name="email-subject"}{intl d='atos.email.default' l="Payment of your order %ref" ref=$order_ref}{/block}
{* Title *}
{block name="email-title"}{intl d='atos.email.default' l="The payment of your order %ref with SIPS Atos is confirmed" ref=$order_ref}{/block}
{* Content *}
{block name="email-content"}
<p>
<a href="{url path="/account"}">
{intl l="View this order in your account at %shop_name" shop_name={config key="store_name"}}
</a>
</p>
<p>{intl d='atos.email.default' l='Thank you again for your purchase.'}</p>
<p>{intl d='atos.email.default' l='The %store_name team.' store_name={config key="store_name"}}</p>
{/block}

View File

@@ -0,0 +1,9 @@
{intl d='atos.email.default' l='Dear customer'},
{intl d='atos.email.default' l='This is a confirmation of the payment of your order %ref with SIPS-Atos on our shop.' ref=$order_ref}
{intl d='atos.email.default' l='Your invoice is now available in your customer account at %url.'} url={config key="url_site"}}
{intl d='atos.email.default' l='Thank you again for your purchase.'}
{intl d='atos.email.default' l='The %store_name team.' store_name={config key="store_name"}}

View File

@@ -0,0 +1,7 @@
<HTML><HEAD><TITLE>ATOS - Paiement Securise sur Internet</TITLE></HEAD>
<BODY bgcolor=#ffffff>
<Font color=#000000>
<center><H1>PAIEMENT SECURISE ATOS </H1></center><br><br>
<center><H1>{$site_name}</H1></center><br><br>
{$form nofilter}
</BODY></HTML>

View File

@@ -0,0 +1,7 @@
<HTML><HEAD><TITLE>ATOS - Paiement Securise sur Internet</TITLE></HEAD>
<BODY bgcolor=#ffffff>
<Font color=#000000>
<center><H1>PAIEMENT SECURISE ATOS </H1></center><br><br>
<center><H1>{$site_name}</H1></center><br><br>
{$form nofilter}
</BODY></HTML>

View File

@@ -0,0 +1,167 @@
<?xml version="1.0" encoding="UTF-8" ?>
<config xmlns="http://thelia.net/schema/dic/config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://thelia.net/schema/dic/config http://thelia.net/schema/dic/config/thelia-1.0.xsd">
<loops>
<loop name="customer_family" class="CustomerFamily\Loop\CustomerFamilyLoop" />
<loop name="customer_customer_family" class="CustomerFamily\Loop\CustomerCustomerFamilyLoop" />
<loop name="customer_family_price" class="CustomerFamily\Loop\CustomerFamilyPriceLoop" />
<loop name="product_purchase_price" class="CustomerFamily\Loop\ProductPurchasePriceLoop" />
<loop name="customer_family_pse_calculated_prices" class="CustomerFamily\Loop\CustomerFamilyProductPriceLoop" />
</loops>
<forms>
<form name="customer.family.create.form" class="CustomerFamily\Form\CustomerFamilyCreateForm" />
<form name="customer.family.update.form" class="CustomerFamily\Form\CustomerFamilyUpdateForm" />
<form name="customer_family_update_default_form" class="CustomerFamily\Form\CustomerFamilyUpdateDefaultForm" />
<form name="customer.family.delete.form" class="CustomerFamily\Form\CustomerFamilyDeleteForm" />
<form name="customer.customer.family.form" class="CustomerFamily\Form\CustomerCustomerFamilyForm" />
<form name="customer_family_price_update" class="CustomerFamily\Form\CustomerFamilyPriceForm" />
<form name="customer_family_price_mode" class="CustomerFamily\Form\CustomerFamilyPriceModeForm" />
</forms>
<services>
<!-- Service class -->
<service id="customer.family.service" class="CustomerFamily\Service\CustomerFamilyService">
<argument type="service" id="thelia.securityContext"/>
<argument type="service" id="thelia.taxEngine"/>
</service>
<!-- Main listener -->
<service id="customer.family.action" class="CustomerFamily\EventListeners\CustomerFamilyListener">
<argument type="service" id="request" />
<argument type="service" id="thelia.parser" />
<argument type="service" id="mailer"/>
<tag name="kernel.event_subscriber"/>
</service>
<!-- Listening to create and update customer form actions -->
<service id="customer.family.form.action" class="CustomerFamily\EventListeners\CustomerFamilyFormListener">
<argument type="service" id="request" />
<tag name="kernel.event_subscriber"/>
</service>
<!-- Listening product creation -->
<service id="customer.family.product.creation" class="CustomerFamily\EventListeners\ProductCreationFormListener">
<argument type="service" id="request" />
<tag name="kernel.event_subscriber"/>
</service>
<!-- Listening product price edition -->
<service id="customer.family.product.purchase.price" class="CustomerFamily\EventListeners\PseExtendPriceFormListener">
<tag name="kernel.event_subscriber"/>
</service>
<!-- Listening PSE price edition -->
<service id="customer.family.pse.purchase.price" class="CustomerFamily\EventListeners\ProductExtendPriceFormListener">
<tag name="kernel.event_subscriber"/>
</service>
<!-- Listening product loop creation -->
<service id="customer.family.price" class="CustomerFamily\EventListeners\CustomerFamilyPriceListener">
<argument type="service" id="thelia.securityContext"/>
<argument type="service" id="thelia.taxEngine"/>
<argument type="service" id="customer.family.service"/>
<tag name="kernel.event_subscriber"/>
</service>
<!-- Listening cart product adding & loop creation -->
<service id="customer.family.cart" class="CustomerFamily\EventListeners\CustomerFamilyCartListener">
<argument type="service" id="customer.family.service"/>
<tag name="kernel.event_subscriber"/>
</service>
<!-- Listening order creation -->
<service id="customer.family.order" class="CustomerFamily\EventListeners\CustomerFamilyOrderListener">
<argument type="service" id="customer.family.service"/>
<tag name="kernel.event_subscriber"/>
</service>
<!-- Listening customer login / logout tu update cart items prices -->
<service id="customer.family.customer.connection" class="CustomerFamily\EventListeners\CustomerFamilyCustomerConnectionListener">
<argument type="service" id="request" />
<argument type="service" id="customer.family.service"/>
<tag name="kernel.event_subscriber"/>
</service>
<service id="customer.family.category.loop_extend" class="CustomerFamily\LoopExtend\CategoryLoopExtend">
<argument type="service" id="thelia.securityContext"/>
<tag name="kernel.event_subscriber"/>
</service>
<service id="customer.family.brand.loop_extend" class="CustomerFamily\LoopExtend\BrandLoopExtend">
<argument type="service" id="thelia.securityContext"/>
<tag name="kernel.event_subscriber"/>
</service>
<service id="customer.family.product.loop_extend" class="CustomerFamily\LoopExtend\ProductLoopExtend">
<argument type="service" id="thelia.securityContext"/>
<tag name="kernel.event_subscriber"/>
</service>
</services>
<hooks>
<!-- Global hook class -->
<hook id="customer.family.hook" class="CustomerFamily\Hook\CustomerFamilyHook">
<tag name="hook.event_listener" event="main.head-css" type="back" method="onAddCss"/>
</hook>
<!-- Admin configuration templates -->
<hook id="customer.family.config">
<tag name="hook.event_listener" event="module.configuration" type="back" templates="render:module_configuration.html" />
<tag name="hook.event_listener" event="module.config-js" type="back" templates="render:assets/js/module-config-js.html" />
<tag name="hook.event_listener" event="categories.js" type="back" templates="js:assets/js/product-creation-price.js" />
</hook>
<hook id="customer.family.menutools.hook" class="CustomerFamily\Hook\CustomerFamilyToolsHook" scope="request">
<tag name="hook.event_listener" event="main.top-menu-tools" type="back" method="onMainTopMenuTools" />
</hook>
<!-- Admin customer create -->
<hook id="customer.family.customer.create">
<tag name="hook.event_listener" event="customer.create-form" type="back" templates="render:customer-create.html" />
<tag name="hook.event_listener" event="customers.js" type="back" templates="js:assets/js/customer-create.js" />
</hook>
<!-- Admin customer edit -->
<hook id="customer.family.customer.edit">
<tag name="hook.event_listener" event="customer.edit" type="back" templates="render:customer-edit.html" />
</hook>
<!-- Admin product creation -->
<hook id="customer.family.product.create">
<tag name="hook.event_listener" event="product.create-form" type="back" templates="render:product-create-form.html"/>
</hook>
<!-- Admin product edit purchase price -->
<hook id="customer.family.product.price.edit" class="CustomerFamily\Hook\CustomerFamilyProductPriceHook">
<tag name="hook.event_listener" event="product.combinations-row" type="back" method="onPsePriceEdit" />
<tag name="hook.event_listener" event="product.details-pricing-form" type="back" templates="render:product-details-pricing.html" />
<tag name="hook.event_listener" event="product.details-promotion-form" type="back" templates="render:product-details-promo.html" />
</hook>
<!-- Additional fields for the register form -->
<!--<hook id="customer.family.register.form" class="CustomerFamily\Hook\CustomerFamilyRegisterFormHook">
<tag name="hook.event_listener" event="register.form-bottom" type="front" method="onRegisterFormBottom"/>
<tag name="hook.event_listener" event="register.after-javascript-include" type="front" method="onRegisterAfterJSInclude"/>
</hook>-->
<!-- Showing customer family information -->
<hook id="customer.family.account.display" class="CustomerFamily\Hook\CustomerFamilyAccountDisplayHook">
<tag name="hook.event_listener" event="account.additional" type="front" method="onAccountAdditional"/>
</hook>
<!-- Additional fields for the account update form -->
<!--<hook id="customer.family.update.form" class="CustomerFamily\Hook\CustomerFamilyUpdateFormHook">
<tag name="hook.event_listener" event="account-update.form-bottom" type="front" method="onAccountUpdateFormBottom"/>
<tag name="hook.event_listener" event="account-update.after-javascript-include" type="front" method="onAccountUpdateAfterJSInclude"/>
</hook>-->
</hooks>
</config>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<module>
<fullnamespace>CustomerFamily\CustomerFamily</fullnamespace>
<descriptive locale="en_US">
<title>Customer Family and Purchase Price</title>
</descriptive>
<descriptive locale="fr_FR">
<title>Famille de clients et prix d'achat</title>
</descriptive>
<version>1.5.5</version>
<author>
<name>Guillaume Barral / Etienne Perriere</name>
<email>gbarral@openstudio.fr / eperriere@openstudio.fr</email>
</author>
<type>classic</type>
<thelia>2.3.0</thelia>
<stability>prod</stability>
</module>

View File

@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
<route id="customer.family.config.view" path="/admin/module/CustomerFamily">
<default key="_controller">CustomerFamily\Controller\Admin\CustomerFamilyAdminController::viewAction</default>
</route>
<!-- Customer Family -->
<route id="customer.family.create" path="/admin/module/CustomerFamily/create" methods="post">
<default key="_controller">CustomerFamily\Controller\Admin\CustomerFamilyAdminController::createAction</default>
</route>
<route id="customer.family.update" path="/admin/module/CustomerFamily/update/{id}" methods="post">
<default key="_controller">CustomerFamily\Controller\Admin\CustomerFamilyAdminController::updateAction</default>
<requirement key="id">\d+</requirement>
</route>
<route id="customer.family.update.default" path="/admin/module/CustomerFamily/update-default" methods="post">
<default key="_controller">CustomerFamily\Controller\Admin\CustomerFamilyAdminController::updateDefaultAction</default>
</route>
<route id="customer.family.delete" path="/admin/module/CustomerFamily/delete/{id}" methods="post">
<default key="_controller">CustomerFamily\Controller\Admin\CustomerFamilyAdminController::deleteAction</default>
<requirement key="id">\d+</requirement>
</route>
<route id="customer.family.customer.update" path="/admin/module/CustomerFamily/customer/update" methods="post">
<default key="_controller">CustomerFamily\Controller\Admin\CustomerFamilyAdminController::customerUpdateAction</default>
</route>
<!-- Prices management -->
<route id="customer.family.price.update" path="/admin/module/CustomerFamily/update-price-calculation" methods="post">
<default key="_controller">CustomerFamily\Controller\Admin\CustomerFamilyPriceController::updateAction</default>
</route>
<route id="customer.family.price.calculate" path="/admin/module/CustomerFamily/calculate-all-prices" methods="get">
<default key="_controller">CustomerFamily\Controller\Admin\CustomerFamilyPriceController::calculatePricesAction</default>
</route>
<route id="customer.family.price.mode.update" path="/admin/CustomerFamily/selectPriceMode" methods="post">
<default key="_controller">CustomerFamily\Controller\Admin\CustomerFamilyPriceController::updatePriceModeAction</default>
</route>
<route id="customer.family.category_restriction" path="/admin/module/CustomerFamily/category_restriction/{customerFamilyId}" methods="post">
<default key="_controller">CustomerFamily\Controller\Admin\CustomerFamilyAdminController::saveCustomerFamilyCategoryRestriction</default>
<requirement key="customerFamilyId">\d+</requirement>
</route>
<route id="customer.family.brand_restriction" path="/admin/module/CustomerFamily/brand_restriction/{customerFamilyId}" methods="post">
<default key="_controller">CustomerFamily\Controller\Admin\CustomerFamilyAdminController::saveCustomerFamilyBrandRestriction</default>
<requirement key="customerFamilyId">\d+</requirement>
</route>
</routes>

View File

@@ -0,0 +1,116 @@
<?xml version="1.0" encoding="UTF-8"?>
<database defaultIdMethod="native" name="thelia" namespace="CustomerFamily\Model">
<table name="customer_family">
<column autoIncrement="true" name="id" primaryKey="true" required="true" type="INTEGER" />
<column name="code" required="true" size="45" type="VARCHAR" />
<column name="title" size="255" type="VARCHAR" />
<column name="category_restriction_enabled" type="TINYINT" defaultValue="0" />
<column name="brand_restriction_enabled" type="TINYINT" defaultValue="0" />
<unique>
<unique-column name="code" />
</unique>
<column name="is_default" type="TINYINT" />
<behavior name="timestampable" />
<behavior name="i18n">
<parameter name="i18n_columns" value="title" />
</behavior>
</table>
<table name="customer_customer_family">
<column name="customer_id" primaryKey="true" required="true" type="INTEGER" />
<column name="customer_family_id" required="true" type="INTEGER" />
<column name="siret" size="50" type="VARCHAR" />
<column name="vat" size="50" type="VARCHAR" />
<foreign-key foreignTable="customer" onDelete="CASCADE">
<reference local="customer_id" foreign="id" />
</foreign-key>
<foreign-key foreignTable="customer_family" onDelete="CASCADE">
<reference local="customer_family_id" foreign="id" />
</foreign-key>
<index name="idx_customer_customer_family_customer_family_id">
<index-column name="customer_family_id" />
</index>
</table>
<table name="customer_family_price">
<column name="customer_family_id" primaryKey="true" required="true" type="INTEGER" />
<foreign-key foreignTable="customer_family" name="fk_customer_family_id" onDelete="CASCADE" onUpdate="RESTRICT">
<reference foreign="id" local="customer_family_id" />
</foreign-key>
<column name="promo" primaryKey="true" type="TINYINT" required="true" defaultValue="0"/>
<column name="use_equation" type="TINYINT" required="true" defaultValue="0"/>
<column name="amount_added_before" type="DECIMAL" size="16" scale="6" defaultValue="0" />
<column name="amount_added_after" type="DECIMAL" size="16" scale="6" defaultValue="0" />
<column name="multiplication_coefficient" type="DECIMAL" size="16" scale="6" defaultValue="1" />
<column name="is_taxed" type="TINYINT" required="true" defaultValue="1"/>
</table>
<table name="product_purchase_price">
<column name="product_sale_elements_id" primaryKey="true" required="true" type="INTEGER" />
<foreign-key foreignTable="product_sale_elements" name="fk_product_sale_elements_id" onDelete="CASCADE" onUpdate="RESTRICT">
<reference foreign="id" local="product_sale_elements_id" />
</foreign-key>
<column name="currency_id" primaryKey="true" required="true" type="INTEGER" />
<foreign-key foreignTable="currency" name="fk_currency_id" onDelete="CASCADE" onUpdate="RESTRICT">
<reference foreign="id" local="currency_id" />
</foreign-key>
<column name="purchase_price" type="DECIMAL" size="16" scale="6" defaultValue="0"/>
</table>
<table name="order_product_purchase_price">
<column name="order_product_id" primaryKey="true" required="true" type="INTEGER" />
<foreign-key foreignTable="order_product" name="fk_order_product_id" onDelete="CASCADE" onUpdate="RESTRICT">
<reference foreign="id" local="order_product_id" />
</foreign-key>
<column name="purchase_price" type="DECIMAL" size="16" scale="6" defaultValue="0"/>
<column name="sale_day_equation" type="LONGVARCHAR" required="true"/>
</table>
<table name="customer_family_order">
<column name="order_id" primaryKey="true" required="true" type="INTEGER"/>
<foreign-key foreignTable="order" name="fk_customer_family_order_customer_family_order_id" onDelete="CASCADE">
<reference foreign="id" local="order_id" />
</foreign-key>
<column name="customer_family_id" required="true" type="INTEGER"/>
<foreign-key foreignTable="customer_family" name="fk_customer_family_order_customer_family_id" onDelete="NONE" onUpdate="CASCADE">
<reference foreign="id" local="customer_family_id" />
</foreign-key>
</table>
<table name="customer_family_available_category">
<column name="customer_family_id" primaryKey="true" required="true" type="INTEGER"/>
<column name="category_id" primaryKey="true" required="true" type="INTEGER"/>
<foreign-key foreignTable="customer_family" name="fk_customer_family_available_category_customer_family_id" onDelete="CASCADE" onUpdate="CASCADE">
<reference foreign="id" local="customer_family_id" />
</foreign-key>
<foreign-key foreignTable="category" name="fk_customer_family_available_category_category_id" onDelete="CASCADE" onUpdate="CASCADE">
<reference foreign="id" local="category_id" />
</foreign-key>
<index name="idx_customer_family_available_category_customer_family_id">
<index-column name="customer_family_id" />
</index>
<index name="idx_customer_family_available_category_category_id">
<index-column name="category_id" />
</index>
</table>
<table name="customer_family_available_brand">
<column name="customer_family_id" primaryKey="true" required="true" type="INTEGER"/>
<column name="brand_id" primaryKey="true" required="true" type="INTEGER"/>
<foreign-key foreignTable="customer_family" name="fk_customer_family_available_brand_customer_family_id" onDelete="CASCADE" onUpdate="CASCADE">
<reference foreign="id" local="customer_family_id" />
</foreign-key>
<foreign-key foreignTable="brand" name="fk_customer_family_available_brand_brand_id" onDelete="CASCADE" onUpdate="CASCADE">
<reference foreign="id" local="brand_id" />
</foreign-key>
<index name="idx_customer_family_available_brand_customer_family_id">
<index-column name="customer_family_id" />
</index>
<index name="idx_customer_family_available_brand_brand_id">
<index-column name="brand_id" />
</index>
</table>
<external-schema filename="local/config/schema.xml" referenceOnly="true" />
</database>

View File

@@ -0,0 +1,2 @@
# Sqlfile -> Database map
thelia.sql=thelia

View File

@@ -0,0 +1,207 @@
# This is a fix for InnoDB in MySQL >= 4.1.x
# It "suspends judgement" for fkey relationships until are tables are set.
SET FOREIGN_KEY_CHECKS = 0;
-- ---------------------------------------------------------------------
-- customer_family
-- ---------------------------------------------------------------------
DROP TABLE IF EXISTS `customer_family`;
CREATE TABLE `customer_family`
(
`id` INTEGER NOT NULL AUTO_INCREMENT,
`code` VARCHAR(45) NOT NULL,
`category_restriction_enabled` TINYINT DEFAULT 0,
`brand_restriction_enabled` TINYINT DEFAULT 0,
`is_default` TINYINT,
`created_at` DATETIME,
`updated_at` DATETIME,
PRIMARY KEY (`id`),
UNIQUE INDEX `customer_family_U_1` (`code`)
) ENGINE=InnoDB;
-- ---------------------------------------------------------------------
-- customer_customer_family
-- ---------------------------------------------------------------------
DROP TABLE IF EXISTS `customer_customer_family`;
CREATE TABLE `customer_customer_family`
(
`customer_id` INTEGER NOT NULL,
`customer_family_id` INTEGER NOT NULL,
`siret` VARCHAR(50),
`vat` VARCHAR(50),
PRIMARY KEY (`customer_id`),
INDEX `idx_customer_customer_family_customer_family_id` (`customer_family_id`),
CONSTRAINT `customer_customer_family_FK_1`
FOREIGN KEY (`customer_id`)
REFERENCES `customer` (`id`)
ON DELETE CASCADE,
CONSTRAINT `customer_customer_family_FK_2`
FOREIGN KEY (`customer_family_id`)
REFERENCES `customer_family` (`id`)
ON DELETE CASCADE
) ENGINE=InnoDB;
-- ---------------------------------------------------------------------
-- customer_family_price
-- ---------------------------------------------------------------------
DROP TABLE IF EXISTS `customer_family_price`;
CREATE TABLE `customer_family_price`
(
`customer_family_id` INTEGER NOT NULL,
`promo` TINYINT DEFAULT 0 NOT NULL,
`use_equation` TINYINT DEFAULT 0 NOT NULL,
`amount_added_before` DECIMAL(16,6) DEFAULT 0,
`amount_added_after` DECIMAL(16,6) DEFAULT 0,
`multiplication_coefficient` DECIMAL(16,6) DEFAULT 1,
`is_taxed` TINYINT DEFAULT 1 NOT NULL,
PRIMARY KEY (`customer_family_id`,`promo`),
CONSTRAINT `fk_customer_family_id`
FOREIGN KEY (`customer_family_id`)
REFERENCES `customer_family` (`id`)
ON UPDATE RESTRICT
ON DELETE CASCADE
) ENGINE=InnoDB;
-- ---------------------------------------------------------------------
-- product_purchase_price
-- ---------------------------------------------------------------------
DROP TABLE IF EXISTS `product_purchase_price`;
CREATE TABLE `product_purchase_price`
(
`product_sale_elements_id` INTEGER NOT NULL,
`currency_id` INTEGER NOT NULL,
`purchase_price` DECIMAL(16,6) DEFAULT 0,
PRIMARY KEY (`product_sale_elements_id`,`currency_id`),
INDEX `FI_currency_id` (`currency_id`),
CONSTRAINT `fk_product_sale_elements_id`
FOREIGN KEY (`product_sale_elements_id`)
REFERENCES `product_sale_elements` (`id`)
ON UPDATE RESTRICT
ON DELETE CASCADE,
CONSTRAINT `fk_currency_id`
FOREIGN KEY (`currency_id`)
REFERENCES `currency` (`id`)
ON UPDATE RESTRICT
ON DELETE CASCADE
) ENGINE=InnoDB;
-- ---------------------------------------------------------------------
-- order_product_purchase_price
-- ---------------------------------------------------------------------
DROP TABLE IF EXISTS `order_product_purchase_price`;
CREATE TABLE `order_product_purchase_price`
(
`order_product_id` INTEGER NOT NULL,
`purchase_price` DECIMAL(16,6) DEFAULT 0,
`sale_day_equation` TEXT NOT NULL,
PRIMARY KEY (`order_product_id`),
CONSTRAINT `fk_order_product_id`
FOREIGN KEY (`order_product_id`)
REFERENCES `order_product` (`id`)
ON UPDATE RESTRICT
ON DELETE CASCADE
) ENGINE=InnoDB;
-- ---------------------------------------------------------------------
-- customer_family_order
-- ---------------------------------------------------------------------
DROP TABLE IF EXISTS `customer_family_order`;
CREATE TABLE `customer_family_order`
(
`order_id` INTEGER NOT NULL,
`customer_family_id` INTEGER NOT NULL,
PRIMARY KEY (`order_id`),
INDEX `FI_customer_family_order_customer_family_id` (`customer_family_id`),
CONSTRAINT `fk_customer_family_order_customer_family_order_id`
FOREIGN KEY (`order_id`)
REFERENCES `order` (`id`)
ON DELETE CASCADE,
CONSTRAINT `fk_customer_family_order_customer_family_id`
FOREIGN KEY (`customer_family_id`)
REFERENCES `customer_family` (`id`)
ON UPDATE CASCADE
) ENGINE=InnoDB;
-- ---------------------------------------------------------------------
-- customer_family_available_category
-- ---------------------------------------------------------------------
DROP TABLE IF EXISTS `customer_family_available_category`;
CREATE TABLE `customer_family_available_category`
(
`customer_family_id` INTEGER NOT NULL,
`category_id` INTEGER NOT NULL,
PRIMARY KEY (`customer_family_id`,`category_id`),
INDEX `idx_customer_family_available_category_customer_family_id` (`customer_family_id`),
INDEX `idx_customer_family_available_category_category_id` (`category_id`),
CONSTRAINT `fk_customer_family_available_category_customer_family_id`
FOREIGN KEY (`customer_family_id`)
REFERENCES `customer_family` (`id`)
ON UPDATE CASCADE
ON DELETE CASCADE,
CONSTRAINT `fk_customer_family_available_category_category_id`
FOREIGN KEY (`category_id`)
REFERENCES `category` (`id`)
ON UPDATE CASCADE
ON DELETE CASCADE
) ENGINE=InnoDB;
-- ---------------------------------------------------------------------
-- customer_family_available_brand
-- ---------------------------------------------------------------------
DROP TABLE IF EXISTS `customer_family_available_brand`;
CREATE TABLE `customer_family_available_brand`
(
`customer_family_id` INTEGER NOT NULL,
`brand_id` INTEGER NOT NULL,
PRIMARY KEY (`customer_family_id`,`brand_id`),
INDEX `idx_customer_family_available_brand_customer_family_id` (`customer_family_id`),
INDEX `idx_customer_family_available_brand_brand_id` (`brand_id`),
CONSTRAINT `fk_customer_family_available_brand_customer_family_id`
FOREIGN KEY (`customer_family_id`)
REFERENCES `customer_family` (`id`)
ON UPDATE CASCADE
ON DELETE CASCADE,
CONSTRAINT `fk_customer_family_available_brand_brand_id`
FOREIGN KEY (`brand_id`)
REFERENCES `brand` (`id`)
ON UPDATE CASCADE
ON DELETE CASCADE
) ENGINE=InnoDB;
-- ---------------------------------------------------------------------
-- customer_family_i18n
-- ---------------------------------------------------------------------
DROP TABLE IF EXISTS `customer_family_i18n`;
CREATE TABLE `customer_family_i18n`
(
`id` INTEGER NOT NULL,
`locale` VARCHAR(5) DEFAULT 'en_US' NOT NULL,
`title` VARCHAR(255),
PRIMARY KEY (`id`,`locale`),
CONSTRAINT `customer_family_i18n_FK_1`
FOREIGN KEY (`id`)
REFERENCES `customer_family` (`id`)
ON DELETE CASCADE
) ENGINE=InnoDB;
# This restores the fkey checks, after having unset them earlier
SET FOREIGN_KEY_CHECKS = 1;

View File

@@ -0,0 +1,16 @@
CREATE TABLE IF NOT EXISTS `customer_family_order`
(
`order_id` INTEGER NOT NULL,
`customer_family_id` INTEGER NOT NULL,
PRIMARY KEY (`order_id`),
INDEX `FI_customer_family_order_customer_family_id` (`customer_family_id`),
CONSTRAINT `fk_customer_family_order_customer_family_order_id`
FOREIGN KEY (`order_id`)
REFERENCES `order` (`id`)
ON DELETE CASCADE,
CONSTRAINT `fk_customer_family_order_customer_family_id`
FOREIGN KEY (`customer_family_id`)
REFERENCES `customer_family` (`id`)
ON UPDATE CASCADE
) ENGINE=InnoDB;

View File

@@ -0,0 +1,93 @@
# This is a fix for InnoDB in MySQL >= 4.1.x
# It "suspends judgement" for fkey relationships until are tables are set.
SET FOREIGN_KEY_CHECKS = 0;
-- ---------------------------------------------------------------------
-- customer_family
-- ---------------------------------------------------------------------
ALTER TABLE `customer_family` ADD `is_default` TINYINT AFTER `code`;
-- ---------------------------------------------------------------------
-- customer_family_price
-- ---------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `customer_family_price`
(
`customer_family_id` INTEGER NOT NULL,
`promo` TINYINT DEFAULT 0 NOT NULL,
`use_equation` TINYINT DEFAULT 0 NOT NULL,
`amount_added_before` DECIMAL(16,6) DEFAULT 0,
`amount_added_after` DECIMAL(16,6) DEFAULT 0,
`multiplication_coefficient` DECIMAL(16,6) DEFAULT 1,
`is_taxed` TINYINT DEFAULT 1 NOT NULL,
PRIMARY KEY (`customer_family_id`,`promo`),
CONSTRAINT `fk_customer_family_id`
FOREIGN KEY (`customer_family_id`)
REFERENCES `customer_family` (`id`)
ON UPDATE RESTRICT
ON DELETE CASCADE
) ENGINE=InnoDB;
-- ---------------------------------------------------------------------
-- product_purchase_price
-- ---------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `product_purchase_price`
(
`product_sale_elements_id` INTEGER NOT NULL,
`currency_id` INTEGER NOT NULL,
`purchase_price` DECIMAL(16,6) DEFAULT 0,
PRIMARY KEY (`product_sale_elements_id`,`currency_id`),
INDEX `FI_currency_id` (`currency_id`),
CONSTRAINT `fk_product_sale_elements_id`
FOREIGN KEY (`product_sale_elements_id`)
REFERENCES `product_sale_elements` (`id`)
ON UPDATE RESTRICT
ON DELETE CASCADE,
CONSTRAINT `fk_currency_id`
FOREIGN KEY (`currency_id`)
REFERENCES `currency` (`id`)
ON UPDATE RESTRICT
ON DELETE CASCADE
) ENGINE=InnoDB;
-- ---------------------------------------------------------------------
-- order_product_purchase_price
-- ---------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `order_product_purchase_price`
(
`order_product_id` INTEGER NOT NULL,
`purchase_price` DECIMAL(16,6) DEFAULT 0,
`sale_day_equation` TEXT NOT NULL,
PRIMARY KEY (`order_product_id`),
CONSTRAINT `fk_order_product_id`
FOREIGN KEY (`order_product_id`)
REFERENCES `order_product` (`id`)
ON UPDATE RESTRICT
ON DELETE CASCADE
) ENGINE=InnoDB;
-- ---------------------------------------------------------------------
-- customer_family_order
-- ---------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `customer_family_order`
(
`order_id` INTEGER NOT NULL,
`customer_family_code` VARCHAR(45) NOT NULL,
PRIMARY KEY (`order_id`),
INDEX `FI_customer_family_code` (`customer_family_code`),
CONSTRAINT `fk_customer_family_order_id`
FOREIGN KEY (`order_id`)
REFERENCES `order` (`id`)
ON DELETE CASCADE,
CONSTRAINT `fk_customer_family_code`
FOREIGN KEY (`customer_family_code`)
REFERENCES `customer_family` (`code`)
ON UPDATE CASCADE
) ENGINE=InnoDB;
# This restores the fkey checks, after having unset them earlier
SET FOREIGN_KEY_CHECKS = 1;

View File

@@ -0,0 +1,59 @@
# This is a fix for InnoDB in MySQL >= 4.1.x
# It "suspends judgement" for fkey relationships until are tables are set.
SET FOREIGN_KEY_CHECKS = 0;
ALTER TABLE `customer_family` ADD `category_restriction_enabled` TINYINT DEFAULT 0 AFTER `code`;
ALTER TABLE `customer_family` ADD `brand_restriction_enabled` TINYINT DEFAULT 0 AFTER `category_restriction_enabled`;
-- ---------------------------------------------------------------------
-- customer_family_available_category
-- ---------------------------------------------------------------------
DROP TABLE IF EXISTS `customer_family_available_category`;
CREATE TABLE `customer_family_available_category`
(
`customer_family_id` INTEGER NOT NULL,
`category_id` INTEGER NOT NULL,
PRIMARY KEY (`customer_family_id`,`category_id`),
INDEX `idx_customer_family_available_category_customer_family_id` (`customer_family_id`),
INDEX `idx_customer_family_available_category_category_id` (`category_id`),
CONSTRAINT `fk_customer_family_available_category_customer_family_id`
FOREIGN KEY (`customer_family_id`)
REFERENCES `customer_family` (`id`)
ON UPDATE CASCADE
ON DELETE CASCADE,
CONSTRAINT `fk_customer_family_available_category_category_id`
FOREIGN KEY (`category_id`)
REFERENCES `category` (`id`)
ON UPDATE CASCADE
ON DELETE CASCADE
) ENGINE=InnoDB;
-- ---------------------------------------------------------------------
-- customer_family_available_brand
-- ---------------------------------------------------------------------
DROP TABLE IF EXISTS `customer_family_available_brand`;
CREATE TABLE `customer_family_available_brand`
(
`customer_family_id` INTEGER NOT NULL,
`brand_id` INTEGER NOT NULL,
PRIMARY KEY (`customer_family_id`,`brand_id`),
INDEX `idx_customer_family_available_brand_customer_family_id` (`customer_family_id`),
INDEX `idx_customer_family_available_brand_brand_id` (`brand_id`),
CONSTRAINT `fk_customer_family_available_brand_customer_family_id`
FOREIGN KEY (`customer_family_id`)
REFERENCES `customer_family` (`id`)
ON UPDATE CASCADE
ON DELETE CASCADE,
CONSTRAINT `fk_customer_family_available_brand_brand_id`
FOREIGN KEY (`brand_id`)
REFERENCES `brand` (`id`)
ON UPDATE CASCADE
ON DELETE CASCADE
) ENGINE=InnoDB;
# This restores the fkey checks, after having unset them earlier
SET FOREIGN_KEY_CHECKS = 1;

View File

@@ -0,0 +1,450 @@
<?php
/*************************************************************************************/
/* This file is part of the module CustomerFamily */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace CustomerFamily\Controller\Admin;
use CustomerFamily\CustomerFamily;
use CustomerFamily\Event\CustomerCustomerFamilyEvent;
use CustomerFamily\Event\CustomerFamilyEvent;
use CustomerFamily\Event\CustomerFamilyEvents;
use CustomerFamily\Form\CustomerCustomerFamilyForm;
use CustomerFamily\Form\CustomerFamilyCreateForm;
use CustomerFamily\Form\CustomerFamilyDeleteForm;
use CustomerFamily\Form\CustomerFamilyUpdateForm;
use CustomerFamily\Model\CustomerFamilyAvailableBrand;
use CustomerFamily\Model\CustomerFamilyAvailableCategory;
use CustomerFamily\Model\CustomerFamilyQuery;
use Propel\Runtime\Propel;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Thelia\Controller\Admin\BaseAdminController;
use Thelia\Core\HttpFoundation\JsonResponse;
use Thelia\Core\HttpFoundation\Request;
use Thelia\Core\Security\AccessManager;
use Thelia\Core\Security\Resource\AdminResources;
use Thelia\Core\Translation\Translator;
use Thelia\Form\BaseForm;
use Thelia\Form\CustomerUpdateForm;
use Thelia\Form\Exception\FormValidationException;
use Thelia\Model\Customer;
use Thelia\Model\CustomerQuery;
use Thelia\Tools\URL;
/**
* Class CustomerFamilyAdminController
* @package CustomerFamily\Controller\Admin
*/
class CustomerFamilyAdminController extends BaseAdminController
{
public function viewAction($params = [])
{
$categoryRestrictions = [];
$brandRestrictions = [];
$customerFamilies = CustomerFamilyQuery::create()
->find();
$con = Propel::getConnection();
/** @var CustomerFamily $customerFamily */
foreach ($customerFamilies as $customerFamily) {
$categoryRestrictionSql = "SELECT c.id, c.parent, ci18n.title, cfac.`customer_family_id`,
CASE
WHEN cfac.`customer_family_id` IS NOT NULL THEN 1
ELSE 0
END as available
FROM category c
LEFT JOIN `category_i18n` ci18n on c.`id` = ci18n.id AND ci18n.locale = :locale
LEFT JOIN `customer_family_available_category` cfac ON c.`id` = cfac.`category_id` AND cfac.`customer_family_id` = :customerFamilyId";
$stmt = $con->prepare($categoryRestrictionSql);
$stmt->bindValue('locale', $this->getCurrentEditionLocale());
$stmt->bindValue('customerFamilyId', $customerFamily->getId());
$stmt->execute();
$categoryRestrictions[$customerFamily->getId()] = $stmt->fetchAll(\PDO::FETCH_ASSOC);
$brandRestrictionSql = "SELECT b.id, bi18n.title, cfab.`customer_family_id`,
CASE
WHEN cfab.`customer_family_id` IS NOT NULL THEN 1
ELSE 0
END as available
FROM brand b
LEFT JOIN `brand_i18n` bi18n on b.`id` = bi18n.id AND bi18n.locale = :locale
LEFT JOIN `customer_family_available_brand` cfab ON b.`id` = cfab.`brand_id` AND cfab.`customer_family_id` = :customerFamilyId";
$stmt = $con->prepare($brandRestrictionSql);
$stmt->bindValue('locale', $this->getCurrentEditionLocale());
$stmt->bindValue('customerFamilyId', $customerFamily->getId());
$stmt->execute();
$brandRestrictions[$customerFamily->getId()] = $stmt->fetchAll(\PDO::FETCH_ASSOC);
}
return $this->render("customer_family_module_configuration", compact('categoryRestrictions', 'brandRestrictions'));
}
/**
* @param Request $request
* @return mixed|\Thelia\Core\HttpFoundation\Response
*/
public function createAction(Request $request)
{
if (null !== $response = $this->checkAuth(array(AdminResources::MODULE), array('CustomerFamily'), AccessManager::CREATE)) {
return $response;
}
$error = "";
$form = new CustomerFamilyCreateForm($request);
try {
$formValidate = $this->validateForm($form);
$event = new CustomerFamilyEvent();
$event->hydrateByForm($formValidate);
$this->dispatch(CustomerFamilyEvents::CUSTOMER_FAMILY_CREATE, $event);
} catch (\Exception $e) {
$error = $e->getMessage();
}
$message = Translator::getInstance()->trans(
"Customer family was created successfully",
array(),
CustomerFamily::MODULE_DOMAIN
);
return self::renderAdminConfig($form, $message, $error);
}
/**
* @param Request $request
* @param $id
* @return mixed|\Thelia\Core\HttpFoundation\Response
*/
public function updateAction(Request $request, $id)
{
if (null !== $response = $this->checkAuth(array(AdminResources::MODULE), array('CustomerFamily'), AccessManager::UPDATE)) {
return $response;
}
$error = "";
$form = new CustomerFamilyUpdateForm($request);
try {
$formValidate = $this->validateForm($form);
$customerFamily = CustomerFamilyQuery::create()->findPk($id);
if ($customerFamily === null) {
throw new \Exception("Customer Family not found by Id");
}
$event = new CustomerFamilyEvent($customerFamily);
$event->hydrateByForm($formValidate);
$this->dispatch(CustomerFamilyEvents::CUSTOMER_FAMILY_UPDATE, $event);
} catch (\Exception $e) {
$error = $e->getMessage();
}
$message = Translator::getInstance()->trans(
"Customer family was updated successfully",
array(),
CustomerFamily::MODULE_DOMAIN
);
return self::renderAdminConfig($form, $message, $error);
}
/**
* Update default family
* There must be at least one default family
*
* @return mixed|\Symfony\Component\HttpFoundation\Response|static
*/
public function updateDefaultAction()
{
if (null !== $response = $this->checkAuth([AdminResources::MODULE], ['CustomerFamily'], AccessManager::UPDATE)) {
return $response;
}
$error = null;
$ex = null;
$form = $this->createForm('customer_family_update_default_form');
try {
$vForm = $this->validateForm($form);
// Get customer_family to update
$customerFamily = CustomerFamilyQuery::create()->findOneById($vForm->get('customer_family_id')->getData());
// If the customer_family exists
if (null !== $customerFamily) {
// If the family to update is not already the default one
if (!$customerFamily->getIsDefault()) {
// Remove old default family
if (null !== $defaultCustomerFamilies = CustomerFamilyQuery::create()->findByIsDefault(1)) {
/** @var \CustomerFamily\Model\CustomerFamily $defaultCustomerFamily */
foreach ($defaultCustomerFamilies as $defaultCustomerFamily) {
$defaultCustomerFamily
->setIsDefault(0)
->save();
}
}
// Save new default family
$customerFamily
->setIsDefault(1)
->save();
}
}
} catch (FormValidationException $ex) {
$error = $this->createStandardFormValidationErrorMessage($ex);
} catch (\Exception $ex) {
$error = $ex->getMessage();
}
if ($error !== null) {
$this->setupFormErrorContext(
$this->getTranslator()->trans("Error updating default family", [], CustomerFamily::MODULE_DOMAIN),
$error,
$form,
$ex
);
return JsonResponse::create(['error'=>$error], 500);
}
return RedirectResponse::create(URL::getInstance()->absoluteUrl("/admin/module/CustomerFamily"));
}
/**
* @param Request $request
* @param $id
* @return mixed|\Thelia\Core\HttpFoundation\Response
*/
public function deleteAction(Request $request, $id)
{
if (null !== $response = $this->checkAuth(array(AdminResources::MODULE), array('CustomerFamily'), AccessManager::DELETE)) {
return $response;
}
$error = "";
$form = new CustomerFamilyDeleteForm($request);
try {
$formValidate = $this->validateForm($form);
$customerFamily = CustomerFamilyQuery::create()->findPk($id);
if ($customerFamily === null) {
throw new \Exception("Customer Family not found by Id");
}
$event = new CustomerFamilyEvent($customerFamily);
$this->dispatch(CustomerFamilyEvents::CUSTOMER_FAMILY_DELETE, $event);
} catch (\Exception $e) {
$error = $e->getMessage();
}
$message = Translator::getInstance()->trans(
"Customer family was deleted successfully",
array(),
CustomerFamily::MODULE_DOMAIN
);
return self::renderAdminConfig($form, $message, $error);
}
/**
* @param BaseForm $form
* @param string $successMessage
* @param string $errorMessage
* @return \Symfony\Component\HttpFoundation\Response|static
*/
protected function renderAdminConfig($form, $successMessage, $errorMessage)
{
if (!empty($errorMessage)) {
$form->setErrorMessage($errorMessage);
$this->getParserContext()
->addForm($form)
->setGeneralError($errorMessage);
}
//for compatibility 2.0
if (method_exists($this->getSession(), "getFlashBag")) {
if (empty($errorMessage)) {
$this->getSession()->getFlashBag()->add("success", $successMessage);
} else {
$this->getSession()->getFlashBag()->add("danger", $errorMessage);
}
}
return RedirectResponse::create(
URL::getInstance()->absoluteUrl("/admin/module/CustomerFamily")
);
}
/**
* @param Request $request
* @return mixed|\Thelia\Core\HttpFoundation\Response
*/
public function customerUpdateAction(Request $request)
{
if (null !== $response = $this->checkAuth(array(AdminResources::MODULE), array('CustomerFamily'), AccessManager::UPDATE)) {
return $response;
}
$error = "";
$form = new CustomerCustomerFamilyForm($request);
try {
$formValidate = $this->validateForm($form);
$event = new CustomerCustomerFamilyEvent($formValidate->get('customer_id')->getData());
$event
->setCustomerFamilyId($formValidate->get('customer_family_id')->getData())
->setSiret($formValidate->get('siret')->getData())
->setVat($formValidate->get('vat')->getData())
;
$this->dispatch(CustomerFamilyEvents::CUSTOMER_CUSTOMER_FAMILY_UPDATE, $event);
return $this->generateRedirect(URL::getInstance()->absoluteUrl(
'/admin/customer/update?customer_id='.$formValidate->get('customer_id')->getData()
));
} catch (FormValidationException $ex) {
$error = $this->createStandardFormValidationErrorMessage($ex);
} catch (\Exception $e) {
$error = $e->getMessage();
}
if (!empty($error)) {
$form->setErrorMessage($error);
}
$this->getParserContext()
->addForm($form)
->setGeneralError($error);
//Don't forget to fill the Customer form
$customerId = $request->get('customer_customer_family_form')['customer_id'];
if (null != $customer = CustomerQuery::create()->findPk($customerId)) {
$customerForm = $this->hydrateCustomerForm($customer);
$this->getParserContext()->addForm($customerForm);
}
return $this->render('customer-edit', array(
'customer_id' => $request->get('customer_customer_family_form')['customer_id'],
"order_creation_error" => Translator::getInstance()->trans($error, array(), CustomerFamily::MESSAGE_DOMAIN)
));
}
public function saveCustomerFamilyCategoryRestriction($customerFamilyId)
{
$customerFamily = CustomerFamilyQuery::create()
->findOneById($customerFamilyId);
$restrictionEnabled = $this->getRequest()->get('restriction_enabled') === "on";
$customerFamily->setCategoryRestrictionEnabled($restrictionEnabled);
$customerFamily->save();
$con = Propel::getConnection();
$deleteSql = "DELETE FROM `customer_family_available_category` WHERE `customer_family_available_category`.`customer_family_id` = :customerFamilyId";
$stmt = $con->prepare($deleteSql);
$stmt->bindValue('customerFamilyId', $customerFamilyId);
$stmt->execute();
$brands = $this->getRequest()->get('available_categories');
if (is_array($brands)) {
foreach ($this->getRequest()->get('available_categories') as $availableCategoryId) {
var_dump($customerFamilyId);
var_dump($availableCategoryId);
$customerFamilyAvailableCategory = new CustomerFamilyAvailableCategory();
$customerFamilyAvailableCategory->setCustomerFamilyId($customerFamilyId)
->setCategoryId($availableCategoryId)
->save();
}
}
return $this->renderAdminConfig(null, "", "");
}
public function saveCustomerFamilyBrandRestriction($customerFamilyId)
{
$customerFamily = CustomerFamilyQuery::create()
->findOneById($customerFamilyId);
$restrictionEnabled = $this->getRequest()->get('restriction_enabled') === "on";
$customerFamily->setBrandRestrictionEnabled($restrictionEnabled);
$customerFamily->save();
$con = Propel::getConnection();
$deleteSql = "DELETE FROM `customer_family_available_brand` WHERE `customer_family_available_brand`.`customer_family_id` = :customerFamilyId";
$stmt = $con->prepare($deleteSql);
$stmt->bindValue('customerFamilyId', $customerFamilyId);
$stmt->execute();
$categories = $this->getRequest()->get('available_categories');
if (is_array($categories)) {
foreach ($this->getRequest()->get('available_categories') as $availableBrandId) {
var_dump($customerFamilyId);
var_dump($availableBrandId);
$customerFamilyAvailableBrand = new CustomerFamilyAvailableBrand();
$customerFamilyAvailableBrand->setCustomerFamilyId($customerFamilyId)
->setBrandId($availableBrandId)
->save();
}
}
return $this->renderAdminConfig(null, "", "");
}
/**
* @param Customer $customer
* @return CustomerUpdateForm
*/
protected function hydrateCustomerForm(Customer $customer)
{
// Get default adress of the customer
$address = $customer->getDefaultAddress();
// Prepare the data that will hydrate the form
$data = array(
'id' => $customer->getId(),
'firstname' => $customer->getFirstname(),
'lastname' => $customer->getLastname(),
'email' => $customer->getEmail(),
'title' => $customer->getTitleId(),
'discount' => $customer->getDiscount(),
'reseller' => $customer->getReseller(),
);
if ($address !== null) {
$data['company'] = $address->getCompany();
$data['address1'] = $address->getAddress1();
$data['address2'] = $address->getAddress2();
$data['address3'] = $address->getAddress3();
$data['phone'] = $address->getPhone();
$data['cellphone'] = $address->getCellphone();
$data['zipcode'] = $address->getZipcode();
$data['city'] = $address->getCity();
$data['country'] = $address->getCountryId();
}
// A loop is used in the template
return new CustomerUpdateForm($this->getRequest(), 'form', $data);
}
}

View File

@@ -0,0 +1,95 @@
<?php
namespace CustomerFamily\Controller\Admin;
use CustomerFamily\CustomerFamily;
use CustomerFamily\Form\CustomerFamilyPriceModeForm;
use CustomerFamily\Model\CustomerFamilyPrice;
use CustomerFamily\Model\CustomerFamilyPriceQuery;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Thelia\Controller\Admin\BaseAdminController;
use Thelia\Core\Security\AccessManager;
use Thelia\Core\Security\Resource\AdminResources;
use Thelia\Form\Exception\FormValidationException;
use Thelia\Tools\URL;
/**
* Class CustomerFamilyPriceController
* @package CustomerFamily\Controller
* @author Etienne Perriere <eperriere@openstudio.fr>
*/
class CustomerFamilyPriceController extends BaseAdminController
{
/**
* Add or update amounts and factor to calculate prices for customer families
*
* @return mixed|\Symfony\Component\HttpFoundation\Response|\Thelia\Core\HttpFoundation\Response|static
*/
public function updateAction()
{
// Check rights
if (null !== $response = $this->checkAuth(
[AdminResources::MODULE],
['CustomerFamily'],
[AccessManager::VIEW, AccessManager::CREATE, AccessManager::UPDATE]
)) {
return $response;
}
$form = $this->createForm('customer_family_price_update');
$error = null;
$ex = null;
try {
$vForm = $this->validateForm($form);
// If no entry exists for the given CustomerFamilyId & promo, create it
if (null === $customerFamilyPrice = CustomerFamilyPriceQuery::create()
->findPk([$vForm->get('customer_family_id')->getData(), $vForm->get('promo')->getData()])) {
// Create new CustomerFamilyPrice
$customerFamilyPrice = new CustomerFamilyPrice();
$customerFamilyPrice
->setCustomerFamilyId($vForm->get('customer_family_id')->getData())
->setPromo($vForm->get('promo')->getData());
}
// Save data
$customerFamilyPrice
->setUseEquation($vForm->get('use_equation')->getData())
->setAmountAddedBefore($vForm->get('amount_added_before')->getData())
->setAmountAddedAfter($vForm->get('amount_added_after')->getData())
->setMultiplicationCoefficient($vForm->get('coefficient')->getData())
->setIsTaxed($vForm->get('is_taxed')->getData())
->save();
} catch (FormValidationException $ex) {
$error = $this->createStandardFormValidationErrorMessage($ex);
} catch (\Exception $ex) {
$error = $ex->getMessage();
}
if ($error !== null) {
$this->setupFormErrorContext(
$this->getTranslator()->trans("CustomerFamily configuration", [], CustomerFamily::MODULE_DOMAIN),
$error,
$form,
$ex
);
return $this->render('module-configure', ['module_code' => 'CustomerFamily']);
}
return RedirectResponse::create(URL::getInstance()->absoluteUrl("/admin/module/CustomerFamily"));
}
public function updatePriceModeAction()
{
$form = new CustomerFamilyPriceModeForm($this->getRequest());
$vForm = $this->validateForm($form);
$mode = $vForm->get('price_mode')->getData();
CustomerFamily::setConfigValue('customer_family_price_mode', $mode);
return RedirectResponse::create(URL::getInstance()->absoluteUrl("/admin/module/CustomerFamily"));
}
}

View File

@@ -0,0 +1,131 @@
<?php
/*************************************************************************************/
/* This file is part of the module CustomerFamily */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace CustomerFamily;
use CustomerFamily\Model\CustomerFamilyQuery;
use Propel\Runtime\Connection\ConnectionInterface;
use Symfony\Component\Finder\Finder;
use Thelia\Install\Database;
use Thelia\Module\BaseModule;
use CustomerFamily\Model\CustomerFamily as CustomerFamilyModel;
/**
* Class CustomerFamily
* @package CustomerFamily
* @contributor Etienne Perriere <eperriere@openstudio.fr>
*/
class CustomerFamily extends BaseModule
{
/** @cont string */
const MODULE_DOMAIN = 'customerfamily';
/** @cont string */
const MESSAGE_DOMAIN = 'customerfamily';
/** @cont string */
const CUSTOMER_FAMILY_PARTICULAR = "particular";
/** @cont string */
const CUSTOMER_FAMILY_PROFESSIONAL = "professional";
/**
* @param ConnectionInterface $con
*/
public function postActivation(ConnectionInterface $con = null)
{
try {
CustomerFamilyQuery::create()->findOne();
} catch (\Exception $e) {
$database = new Database($con);
$database->insertSql(null, [__DIR__ . "/Config/thelia.sql"]);
}
//Generate the 2 defaults customer_family
//Customer
self::getCustomerFamilyByCode(self::CUSTOMER_FAMILY_PARTICULAR, "Particulier", "fr_FR");
self::getCustomerFamilyByCode(self::CUSTOMER_FAMILY_PARTICULAR, "Private individual", "en_US");
//Professional
self::getCustomerFamilyByCode(self::CUSTOMER_FAMILY_PROFESSIONAL, "Professionnel", "fr_FR");
self::getCustomerFamilyByCode(self::CUSTOMER_FAMILY_PROFESSIONAL, "Professional", "en_US");
}
public function update($currentVersion, $newVersion, ConnectionInterface $con = null)
{
$finder = Finder::create()
->name('*.sql')
->depth(0)
->sortByName()
->in(__DIR__ . DS . 'Config' . DS . 'update');
$database = new Database($con);
/** @var \SplFileInfo $file */
foreach ($finder as $file) {
if (version_compare($currentVersion, $file->getBasename('.sql'), '<')) {
$database->insertSql(null, [$file->getPathname()]);
}
}
}
/**
* @param $code
* @param null $title
* @param string $locale
*
* @return Model\CustomerFamily
*/
public static function getCustomerFamilyByCode($code, $title = null, $locale = "fr_FR")
{
if ($title == null) {
$title = $code;
}
// Set 'particular' as default family
if ($code == self::CUSTOMER_FAMILY_PARTICULAR) {
$isDefault = 1;
} else {
$isDefault = 0;
}
/** @var CustomerFamilyModel $customerFamily */
if (null == $customerFamily = CustomerFamilyQuery::create()
->useCustomerFamilyI18nQuery()
->filterByLocale($locale)
->endUse()
->filterByCode($code)
->findOne()
) {
//Be sure that you don't create it twice
/** @var CustomerFamilyModel $customerF */
if (null != $customerF = CustomerFamilyQuery::create()->findOneByCode($code)) {
$customerF
->setLocale($locale)
->setTitle($title)
->save();
} else {
$customerFamily = new CustomerFamilyModel();
$customerFamily
->setCode($code)
->setIsDefault($isDefault)
->setLocale($locale)
->setTitle($title)
->save();
}
}
return $customerFamily;
}
}

View File

@@ -0,0 +1,123 @@
<?php
/*************************************************************************************/
/* This file is part of the module CustomerFamily */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace CustomerFamily\Event;
use CustomerFamily\Model\CustomerCustomerFamily;
use Thelia\Core\Event\ActionEvent;
/**
* Class CustomerCustomerFamilyEvent
* @package CustomerFamily\Event
*/
class CustomerCustomerFamilyEvent extends ActionEvent
{
/** @var int */
protected $customerId;
/** @var int */
protected $customerFamilyId;
/** @var string */
protected $siret;
/** @var string */
protected $vat;
/**
* @param int $customerId
*/
public function __construct($customerId)
{
$this->customerId = $customerId;
}
/**
* @param int $customerFamilyId
*
* @return CustomerCustomerFamily
*/
public function setCustomerFamilyId($customerFamilyId)
{
$this->customerFamilyId = $customerFamilyId;
return $this;
}
/**
* @return int
*/
public function getCustomerFamilyId()
{
return $this->customerFamilyId;
}
/**
* @param int $customerId
*
* @return CustomerCustomerFamily
*/
public function setCustomerId($customerId)
{
$this->customerId = $customerId;
return $this;
}
/**
* @return int
*/
public function getCustomerId()
{
return $this->customerId;
}
/**
* @param mixed $siret
*
* @return CustomerCustomerFamily
*/
public function setSiret($siret)
{
$this->siret = $siret;
return $this;
}
/**
* @return mixed
*/
public function getSiret()
{
return $this->siret;
}
/**
* @param mixed $vat
*
* @return CustomerCustomerFamily
*/
public function setVat($vat)
{
$this->vat = $vat;
return $this;
}
/**
* @return mixed
*/
public function getVat()
{
return $this->vat;
}
}

View File

@@ -0,0 +1,130 @@
<?php
/*************************************************************************************/
/* This file is part of the module CustomerFamily */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace CustomerFamily\Event;
use CustomerFamily\Model\CustomerFamily;
use Symfony\Component\Form\Form;
use Thelia\Core\Event\ActionEvent;
/**
* Class CustomerFamilyEvent
* @package CustomerFamily\Event
*/
class CustomerFamilyEvent extends ActionEvent
{
/** @var CustomerFamily */
private $customerFamily;
public function __construct(CustomerFamily $customerFamily = null)
{
if ($customerFamily !== null) {
$this->customerFamily = $customerFamily;
} else {
$this->customerFamily = new CustomerFamily();
}
}
/**
* @param CustomerFamily $customerFamily
* @return $this
*/
public function setCustomerFamily(CustomerFamily $customerFamily)
{
$this->customerFamily = $customerFamily;
return $this;
}
/**
* @return CustomerFamily
*/
public function getCustomerFamily()
{
return $this->customerFamily;
}
/**
* @return int
*/
public function getId()
{
return $this->customerFamily->getId();
}
/**
* @return string
*/
public function getCode()
{
return $this->customerFamily->getCode();
}
/**
* @param string $code
* @return $this
*/
public function setCode($code)
{
$this->customerFamily->setCode($code);
return $this;
}
/**
* @param $locale
* @return string
*/
public function getTitle($locale = null)
{
if ($locale === null) {
$locale = $this->customerFamily->getLocale();
}
$this->customerFamily->setLocale($locale);
return $this->customerFamily->getTitle();
}
/**
* @param $title
* @param null $locale
* @return $this
*/
public function setTitle($title, $locale = null)
{
if ($locale === null) {
$locale = $this->customerFamily->getLocale();
}
$this->customerFamily->setLocale($locale);
$this->customerFamily->setTitle($title);
return $this;
}
/**
* @param Form $form
*/
public function hydrateByForm(Form $form)
{
//code
if ($form->get('code') !== null) {
self::setCode($form->get('code')->getData());
}
//title
if ($form->get('title') !== null && $form->get('locale') !== null) {
self::setTitle($form->get('title')->getData(), $form->get('locale')->getData());
}
}
}

View File

@@ -0,0 +1,25 @@
<?php
/*************************************************************************************/
/* This file is part of the module CustomerFamily */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace CustomerFamily\Event;
/**
* Class CustomerFamilyEvents
* @package CustomerFamily\Event
*/
class CustomerFamilyEvents
{
const CUSTOMER_CUSTOMER_FAMILY_UPDATE = "action.front.customer.customer.family.update";
const CUSTOMER_FAMILY_CREATE = "action.admin.customer.family.create";
const CUSTOMER_FAMILY_UPDATE = "action.admin.customer.family.update";
const CUSTOMER_FAMILY_DELETE = "action.admin.customer.family.delete";
}

View File

@@ -0,0 +1,58 @@
<?php
namespace CustomerFamily\EventListeners;
use CustomerFamily\Service\CustomerFamilyService;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Cart\CartEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Model\ProductSaleElementsQuery;
/**
* Class CustomerFamilyCartListener
* @package CustomerFamily\EventListeners
* @author Etienne Perriere <eperriere@openstudio.fr>
*/
class CustomerFamilyCartListener implements EventSubscriberInterface
{
protected $customerFamilyService;
public function __construct(CustomerFamilyService $customerFamilyService)
{
$this->customerFamilyService = $customerFamilyService;
}
/**
* Returns an array of event names this subscriber wants to listen to.
*
* @return array The event names to listen to
*/
public static function getSubscribedEvents()
{
return [
TheliaEvents::CART_ADDITEM => ['addCartItem', 128]
];
}
/**
* @param CartEvent $cartEvent
* @throws \Exception
* @throws \Propel\Runtime\Exception\PropelException
*/
public function addCartItem(CartEvent $cartEvent)
{
$pseId = $cartEvent->getProductSaleElementsId();
$pse = ProductSaleElementsQuery::create()->findOneById($pseId);
$prices = $this->customerFamilyService->calculateCustomerPsePrice($pse);
if ($prices['price'] !== null) {
$cartEvent->getCartItem()->setPrice($prices['price']);
}
if ($prices['promoPrice'] !== null) {
$cartEvent->getCartItem()->setPromoPrice($prices['promoPrice']);
}
$cartEvent->getCartItem()->save();
}
}

View File

@@ -0,0 +1,118 @@
<?php
namespace CustomerFamily\EventListeners;
use CustomerFamily\Service\CustomerFamilyService;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Customer\CustomerLoginEvent;
use Thelia\Core\Event\DefaultActionEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\HttpFoundation\Request;
use Thelia\Model\ProductSaleElementsQuery;
/**
* Class CustomerFamilyCustomerConnectionListener
* @package CustomerFamily\EventListeners
* @author Etienne Perriere <eperriere@openstudio.fr>
*/
class CustomerFamilyCustomerConnectionListener implements EventSubscriberInterface
{
protected $request;
protected $customerFamilyService;
public function __construct(Request $request, CustomerFamilyService $customerFamilyService)
{
$this->request = $request;
$this->customerFamilyService = $customerFamilyService;
}
/**
* Returns an array of event names this subscriber wants to listen to.
*
* @return array The event names to listen to
*/
public static function getSubscribedEvents()
{
return [
TheliaEvents::CUSTOMER_LOGOUT => ['customerLogout', 128],
TheliaEvents::CUSTOMER_LOGIN => ['customerLogin', 128]
];
}
/**
* Update cart items' prices when logging out
*
* @param DefaultActionEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
* @throws \Exception
* @throws \Propel\Runtime\Exception\PropelException
*/
public function customerLogout(DefaultActionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
// Get cart & loop on its items
$cart = $this->request->getSession()->getSessionCart($dispatcher);
/** @var \Thelia\Model\CartItem $cartItem */
foreach ($cart->getCartItems() as $cartItem) {
// Get item's corresponding PSE
$pse = ProductSaleElementsQuery::create()->findOneById($cartItem->getProductSaleElementsId());
// Get pse's prices for the customer
$prices = $this->customerFamilyService->calculateCustomerPsePrice(
$pse,
null,
$cart->getCurrencyId()
);
if ($prices['price'] !== null) {
$cartItem->setPrice($prices['price']);
}
if ($prices['promoPrice'] !== null) {
$cartItem->setPromoPrice($prices['promoPrice']);
}
$cartItem->save();
}
}
/**
* Update cart items' prices when logging in
*
* @param CustomerLoginEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
* @throws \Exception
* @throws \Propel\Runtime\Exception\PropelException
*/
public function customerLogin(CustomerLoginEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
// Get cart & loop on its items
$cart = $this->request->getSession()->getSessionCart($dispatcher);
/** @var \Thelia\Model\CartItem $cartItem */
foreach ($cart->getCartItems() as $cartItem) {
// Get item's corresponding PSE
$pse = ProductSaleElementsQuery::create()->findOneById($cartItem->getProductSaleElementsId());
// Get pse's prices for the customer
$prices = $this->customerFamilyService->calculateCustomerPsePrice(
$pse,
$event->getCustomer()->getId(),
$cart->getCurrencyId()
);
if ($prices['price'] !== null) {
$cartItem->setPrice($prices['price']);
}
if ($prices['promoPrice'] !== null) {
$cartItem->setPromoPrice($prices['promoPrice']);
}
$cartItem->save();
}
}
}

View File

@@ -0,0 +1,254 @@
<?php
/*************************************************************************************/
/* This file is part of the module CustomerFamily */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace CustomerFamily\EventListeners;
use CustomerFamily\CustomerFamily;
use CustomerFamily\Model\CustomerCustomerFamilyQuery;
use CustomerFamily\Model\CustomerFamilyQuery;
use Symfony\Component\Validator\Constraints;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Thelia\Action\BaseAction;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Event\TheliaFormEvent;
use Thelia\Core\HttpFoundation\Request;
use Thelia\Core\Translation\Translator;
class CustomerFamilyFormListener extends BaseAction implements EventSubscriberInterface
{
/** 'thelia_customer_create' is the name of the form used to create Customers (Thelia\Form\CustomerCreateForm). */
const THELIA_CUSTOMER_CREATE_FORM_NAME = 'thelia_customer_create';
const THELIA_ADMIN_CUSTOMER_CREATE_FORM_NAME = 'thelia_customer_create';
/**
* 'thelia_customer_profile_update' is the name of the form used to update accounts
* (Thelia\Form\CustomerProfileUpdateForm).
*/
const THELIA_ACCOUNT_UPDATE_FORM_NAME = 'thelia_customer_profile_update';
const CUSTOMER_FAMILY_CODE_FIELD_NAME = 'customer_family_code';
const CUSTOMER_FAMILY_SIRET_FIELD_NAME = 'siret';
const CUSTOMER_FAMILY_VAT_FIELD_NAME = 'vat';
/** @var \Thelia\Core\HttpFoundation\Request */
protected $request;
/**
* @param Request $request
*/
public function __construct(Request $request)
{
$this->request = $request;
}
/**
* Returns an array of event names this subscriber wants to listen to.
*
* @return array The event names to listen to
*
* @api
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::FORM_AFTER_BUILD.'.'.self::THELIA_CUSTOMER_CREATE_FORM_NAME => array('addCustomerFamilyFieldsForRegister', 128),
TheliaEvents::FORM_AFTER_BUILD.'.'.self::THELIA_ADMIN_CUSTOMER_CREATE_FORM_NAME => array('addCustomerFamilyFieldsForRegister', 128),
TheliaEvents::FORM_AFTER_BUILD.'.'.self::THELIA_ACCOUNT_UPDATE_FORM_NAME => array('addCustomerFamilyFieldsForUpdate', 128),
);
}
/**
* Callback used to add some fields to the Thelia's CustomerCreateForm.
* It add two fields : one for the SIRET number and one for VAT.
* @param TheliaFormEvent $event
*/
public function addCustomerFamilyFieldsForRegister(TheliaFormEvent $event)
{
// Retrieving CustomerFamily choices
$customerFamilyChoices = array();
/** @var \CustomerFamily\Model\CustomerFamily $customerFamily */
foreach (CustomerFamilyQuery::create()->find() as $customerFamily) {
$customerFamilyChoices[$customerFamily->getCode()] = self::trans($customerFamily->getTitle());
}
// Building additional fields
$event->getForm()->getFormBuilder()
->add(
self::CUSTOMER_FAMILY_CODE_FIELD_NAME,
'choice',
array(
'constraints' => array(
new Constraints\Callback(
array(
'methods' => array(
array(
$this, 'checkCustomerFamily'
)
)
)
),
new Constraints\NotBlank(),
),
'choices' => $customerFamilyChoices,
'empty_data' => false,
'required' => false,
'label' => self::trans('Customer family'),
'label_attr' => array(
'for' => 'customer_family_id',
),
'mapped' => false,
)
)
->add(
self::CUSTOMER_FAMILY_SIRET_FIELD_NAME,
'text',
array(
'required' => true,
'empty_data' => false,
'label' => self::trans('Siret number'),
'label_attr' => array(
'for' => 'siret'
),
'mapped' => false,
)
)
->add(
self::CUSTOMER_FAMILY_VAT_FIELD_NAME,
'text',
array(
'required' => true,
'empty_data' => false,
'label' => self::trans('Vat'),
'label_attr' => array(
'for' => 'vat'
),
'mapped' => false,
)
)
;
}
/**
* Callback used to add some fields to the Thelia's CustomerCreateForm.
* It add two fields : one for the SIRET number and one for VAT.
* @param TheliaFormEvent $event
*/
public function addCustomerFamilyFieldsForUpdate(TheliaFormEvent $event)
{
// Adding new fields
$customer = $this->request->getSession()->getCustomerUser();
if (is_null($customer)) {
// No customer => no account update => stop here
return;
}
$customerCustomerFamily = CustomerCustomerFamilyQuery::create()->findOneByCustomerId($customer->getId());
$cfData = array(
self::CUSTOMER_FAMILY_CODE_FIELD_NAME => (is_null($customerCustomerFamily) or is_null($customerCustomerFamily->getCustomerFamily())) ? '' : $customerCustomerFamily->getCustomerFamily()->getCode(),
self::CUSTOMER_FAMILY_SIRET_FIELD_NAME => is_null($customerCustomerFamily) ? false : $customerCustomerFamily->getSiret(),
self::CUSTOMER_FAMILY_VAT_FIELD_NAME => is_null($customerCustomerFamily) ? false : $customerCustomerFamily->getVat(),
);
// Retrieving CustomerFamily choices
$customerFamilyChoices = array();
/** @var \CustomerFamily\Model\CustomerFamily $customerFamilyChoice */
foreach (CustomerFamilyQuery::create()->find() as $customerFamilyChoice) {
$customerFamilyChoices[$customerFamilyChoice->getCode()] = self::trans($customerFamilyChoice->getTitle());
}
// Building additional fields
$event->getForm()->getFormBuilder()
->add(
self::CUSTOMER_FAMILY_CODE_FIELD_NAME,
'choice',
array(
'constraints' => array(
new Constraints\Callback(array('methods' => array(
array($this, 'checkCustomerFamily')
))),
new Constraints\NotBlank(),
),
'choices' => $customerFamilyChoices,
'empty_data' => false,
'required' => false,
'label' => self::trans('Customer family'),
'label_attr' => array(
'for' => 'customer_family_id',
),
'mapped' => false,
'data' => $cfData[self::CUSTOMER_FAMILY_CODE_FIELD_NAME],
)
)
->add(
self::CUSTOMER_FAMILY_SIRET_FIELD_NAME,
'text',
array(
'required' => true,
'empty_data' => false,
'label' => self::trans('Siret number'),
'label_attr' => array(
'for' => 'siret'
),
'mapped' => false,
'data' => $cfData[self::CUSTOMER_FAMILY_SIRET_FIELD_NAME],
)
)
->add(
self::CUSTOMER_FAMILY_VAT_FIELD_NAME,
'text',
array(
'required' => true,
'empty_data' => false,
'label' => self::trans('Vat'),
'label_attr' => array(
'for' => 'vat'
),
'mapped' => false,
'data' => $cfData[self::CUSTOMER_FAMILY_VAT_FIELD_NAME],
)
)
;
}
/**
* Validate a field only if the customer family is valid
*
* @param string $value
* @param ExecutionContextInterface $context
*/
public function checkCustomerFamily($value, ExecutionContextInterface $context)
{
if (CustomerFamilyQuery::create()->filterByCode($value)->count() == 0) {
$context->addViolation(self::trans('The customer family is not valid'));
}
}
/**
* Utility for translations
* @param $id
* @param array $parameters
* @return string
*/
protected static function trans($id, array $parameters = array())
{
return Translator::getInstance()->trans($id, $parameters, CustomerFamily::MESSAGE_DOMAIN);
}
}

View File

@@ -0,0 +1,246 @@
<?php
/*************************************************************************************/
/* This file is part of the module CustomerFamily */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace CustomerFamily\EventListeners;
use CustomerFamily\CustomerFamily;
use CustomerFamily\Event\CustomerCustomerFamilyEvent;
use CustomerFamily\Event\CustomerFamilyEvent;
use CustomerFamily\Event\CustomerFamilyEvents;
use CustomerFamily\Model\CustomerCustomerFamily;
use CustomerFamily\Model\CustomerCustomerFamilyQuery;
use CustomerFamily\Model\CustomerFamilyQuery;
use Propel\Runtime\ActiveQuery\Criteria;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Customer\CustomerCreateOrUpdateEvent;
use Thelia\Core\Event\Customer\CustomerEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\HttpFoundation\Request;
use Thelia\Core\Template\ParserInterface;
use Thelia\Mailer\MailerFactory;
/**
* Class CustomerFamilyListener
* @package CustomerFamily\EventListeners
*/
class CustomerFamilyListener implements EventSubscriberInterface
{
const THELIA_CUSTOMER_CREATE_FORM_NAME = 'thelia_customer_create';
const THELIA_CUSTOMER_UPDATE_FORM_NAME = 'thelia_customer_profile_update';
/** @var \Thelia\Core\HttpFoundation\Request */
protected $request;
/** @var \Thelia\Core\Template\ParserInterface */
protected $parser;
/** @var \Thelia\Mailer\MailerFactory */
protected $mailer;
/**
* @param Request $request
* @param ParserInterface $parser
* @param MailerFactory $mailer
*/
public function __construct(Request $request, ParserInterface $parser, MailerFactory $mailer)
{
$this->request = $request;
$this->parser = $parser;
$this->mailer = $mailer;
}
/**
* Returns an array of event names this subscriber wants to listen to.
*
* @return array The event names to listen to
*
* @api
*/
public static function getSubscribedEvents()
{
return array(
TheliaEvents::AFTER_CREATECUSTOMER => array(
'afterCreateCustomer', 100
),
TheliaEvents::CUSTOMER_UPDATEPROFILE => array(
'customerUpdateProfile', 100
),
CustomerFamilyEvents::CUSTOMER_CUSTOMER_FAMILY_UPDATE => array(
"customerCustomerFamilyUpdate", 128
),
CustomerFamilyEvents::CUSTOMER_FAMILY_CREATE => array(
'create', 128
),
CustomerFamilyEvents::CUSTOMER_FAMILY_UPDATE => array(
'update', 128
),
CustomerFamilyEvents::CUSTOMER_FAMILY_DELETE => array(
'delete', 128
),
);
}
/**
* @param CustomerFamilyEvent $event
* @throws \Exception
* @throws \Propel\Runtime\Exception\PropelException
*/
public function create(CustomerFamilyEvent $event)
{
if (CustomerFamilyQuery::create()
->filterByCode($event->getCode())
->findOne() !== null
) {
throw new \Exception("Customer family code is already in use");
}
$event->getCustomerFamily()->save();
}
/**
* @param CustomerFamilyEvent $event
* @throws \Exception
* @throws \Propel\Runtime\Exception\PropelException
*/
public function update(CustomerFamilyEvent $event)
{
if (CustomerFamilyQuery::create()
->filterByCode($event->getCode())
->filterById($event->getId(), Criteria::NOT_EQUAL)
->findOne() !== null
) {
throw new \Exception("Customer family code is already in use");
}
$event->getCustomerFamily()->save();
}
/**
* @param CustomerFamilyEvent $event
*/
public function delete(CustomerFamilyEvent $event)
{
$event->getCustomerFamily()->delete();
}
/**
* @param CustomerEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function afterCreateCustomer(CustomerEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$form = $this->request->request->get(self::THELIA_CUSTOMER_CREATE_FORM_NAME);
if (is_null($form) or !array_key_exists(CustomerFamilyFormListener::CUSTOMER_FAMILY_CODE_FIELD_NAME, $form)) {
// Nothing to create the new CustomerCustomerFamily => stop here !
return;
}
$customerFamily = CustomerFamilyQuery::create()->findOneByCode($form[CustomerFamilyFormListener::CUSTOMER_FAMILY_CODE_FIELD_NAME]);
if (is_null($customerFamily)) {
// No family => no CustomerCustomerFamily to update.
return;
}
$customerFamilyId = $customerFamily->getId();
// Ignore SIRET and VAT if the customer is not professional
$siret = $customerFamily->getCode() == CustomerFamily::CUSTOMER_FAMILY_PROFESSIONAL ? $form[CustomerFamilyFormListener::CUSTOMER_FAMILY_SIRET_FIELD_NAME] : '';
$vat = $customerFamily->getCode() == CustomerFamily::CUSTOMER_FAMILY_PROFESSIONAL ? $form[CustomerFamilyFormListener::CUSTOMER_FAMILY_VAT_FIELD_NAME] : '';
$updateEvent = new CustomerCustomerFamilyEvent($event->getCustomer()->getId());
$updateEvent
->setCustomerFamilyId($customerFamilyId)
->setSiret($siret)
->setVat($vat)
;
$dispatcher->dispatch(CustomerFamilyEvents::CUSTOMER_CUSTOMER_FAMILY_UPDATE, $updateEvent);
}
/**
* @param CustomerCreateOrUpdateEvent $event
* @param $eventName
* @param EventDispatcherInterface $dispatcher
*/
public function customerUpdateProfile(CustomerCreateOrUpdateEvent $event, $eventName, EventDispatcherInterface $dispatcher)
{
$form = $this->request->request->get(self::THELIA_CUSTOMER_UPDATE_FORM_NAME);
if (is_null($form) or !array_key_exists(CustomerFamilyFormListener::CUSTOMER_FAMILY_CODE_FIELD_NAME, $form)) {
// Nothing to update => stop here !
return;
}
// Erase SIRET and VAT if the customer is now in the 'particular' customer family.
if ($form[CustomerFamilyFormListener::CUSTOMER_FAMILY_CODE_FIELD_NAME] == CustomerFamily::CUSTOMER_FAMILY_PARTICULAR) {
$siret = '';
$vat = '';
} else {
$siret = $form[CustomerFamilyFormListener::CUSTOMER_FAMILY_SIRET_FIELD_NAME];
$vat = $form[CustomerFamilyFormListener::CUSTOMER_FAMILY_VAT_FIELD_NAME];
}
$newCustomerFamily = CustomerFamilyQuery::create()->findOneByCode($form[CustomerFamilyFormListener::CUSTOMER_FAMILY_CODE_FIELD_NAME]);
$updateEvent = new CustomerCustomerFamilyEvent($event->getCustomer()->getId());
$updateEvent
->setCustomerFamilyId($newCustomerFamily->getId())
->setSiret($siret)
->setVat($vat)
;
$dispatcher->dispatch(CustomerFamilyEvents::CUSTOMER_CUSTOMER_FAMILY_UPDATE, $updateEvent);
}
/**
* @param CustomerCustomerFamilyEvent $event
*/
public function customerCustomerFamilyUpdate(CustomerCustomerFamilyEvent $event)
{
$customerCustomerFamily = CustomerCustomerFamilyQuery::create()->findOneByCustomerId($event->getCustomerId());
if ($customerCustomerFamily === null) {
$customerCustomerFamily = new CustomerCustomerFamily();
$customerCustomerFamily
->setCustomerId($event->getCustomerId())
;
}
$customerCustomerFamily
->setCustomerFamilyId($event->getCustomerFamilyId())
->setSiret($event->getSiret())
->setVat($event->getVat())
->save()
;
}
/**
* @return mixed|null
*/
protected function getCustomerFamilyForm()
{
if (null != $form = $this->request->request->get("customer_family_customer_profile_update_form")) {
return $form;
}
if (null != $form = $this->request->request->get(self::THELIA_CUSTOMER_CREATE_FORM_NAME)) {
return $form;
}
return null;
}
}

View File

@@ -0,0 +1,102 @@
<?php
namespace CustomerFamily\EventListeners;
use CustomerFamily\Model\CustomerFamilyOrder;
use CustomerFamily\Model\CustomerFamilyPriceQuery;
use CustomerFamily\Model\CustomerFamilyQuery;
use CustomerFamily\Model\Map\ProductPurchasePriceTableMap;
use CustomerFamily\Model\OrderProductPurchasePrice;
use CustomerFamily\Model\ProductPurchasePrice;
use CustomerFamily\Model\ProductPurchasePriceQuery;
use CustomerFamily\Service\CustomerFamilyService;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Order\OrderEvent;
use Thelia\Core\Event\TheliaEvents;
/**
* Class CustomerFamilyOrderListener
* @package CustomerFamily\EventListeners
* @author Etienne Perriere <eperriere@openstudio.fr>
*/
class CustomerFamilyOrderListener implements EventSubscriberInterface
{
protected $customerFamilyService;
public function __construct(CustomerFamilyService $customerFamilyService)
{
$this->customerFamilyService = $customerFamilyService;
}
/**
* Returns an array of event names this subscriber wants to listen to.
*
* @return array The event names to listen to
*/
public static function getSubscribedEvents()
{
return [
TheliaEvents::ORDER_BEFORE_PAYMENT => ['createOrderPurchasePrices', 128],
TheliaEvents::ORDER_AFTER_CREATE => ['saveOrderFamilyAndEquation', 128]
];
}
/**
* Save purchase price for each product of the order
*
* @param OrderEvent $orderEvent
* @throws \Exception
* @throws \Propel\Runtime\Exception\PropelException
*/
public function createOrderPurchasePrices(OrderEvent $orderEvent)
{
$orderProducts = $orderEvent->getOrder()->getOrderProducts();
$currencyId = $orderEvent->getOrder()->getCurrencyId();
$customerFamilyId = $this->customerFamilyService->getCustomerCustomerFamilyId($orderEvent->getOrder()->getCustomerId());
/** @var \Thelia\Model\OrderProduct $orderProduct */
foreach ($orderProducts as $orderProduct) {
// If a ProductPurchasePrice exists for the PSE & currency
if (null !== $purchasePrice = $this->customerFamilyService
->getPurchasePrice(
$orderProduct->getProductSaleElementsId(),
$currencyId)){
// Initialize equation
$equation = 'Equation not used';
// If equation was used, get information about it
if (null !== $customerFamilyPrice = $this->customerFamilyService->getCustomerFamilyPrice(
$customerFamilyId, $orderProduct->getWasInPromo(), 1)) {
$equation = '( ' . $purchasePrice . ' + ' . $customerFamilyPrice->getAmountAddedBefore() .
' ) x ' . $customerFamilyPrice->getMultiplicationCoefficient() . ' + ' .
$customerFamilyPrice->getAmountAddedAfter();
}
// New OrderProductPurchasePrice
(new OrderProductPurchasePrice())
->setOrderProductId($orderProduct->getId())
->setPurchasePrice($purchasePrice)
->setSaleDayEquation($equation)
->save();
}
}
}
/**
* @param OrderEvent $orderEvent
* @throws \Propel\Runtime\Exception\PropelException
*/
public function saveOrderFamilyAndEquation(OrderEvent $orderEvent)
{
$customerFamily = CustomerFamilyQuery::create()
->findOneById(
$this->customerFamilyService->getCustomerCustomerFamilyId($orderEvent->getOrder()->getCustomerId())
);
(new CustomerFamilyOrder())
->setOrderId($orderEvent->getOrder()->getId())
->setCustomerFamilyId($customerFamily->getId())
->save();
}
}

View File

@@ -0,0 +1,302 @@
<?php
namespace CustomerFamily\EventListeners;
use CustomerFamily\CustomerFamily;
use CustomerFamily\Model\Map\ProductPurchasePriceTableMap;
use CustomerFamily\Service\CustomerFamilyService;
use Propel\Runtime\ActiveQuery\Criteria;
use Propel\Runtime\ActiveQuery\Join;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Thelia\Core\Event\Loop\LoopExtendsBuildModelCriteriaEvent;
use Thelia\Core\Event\Loop\LoopExtendsParseResultsEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Security\SecurityContext;
use Thelia\Exception\TaxEngineException;
use Thelia\Model\Currency;
use Thelia\Model\Map\ProductPriceTableMap;
use Thelia\Model\Map\ProductSaleElementsTableMap;
use Thelia\Model\Product;
use Thelia\Model\ProductPrice;
use Thelia\Model\ProductQuery;
use Thelia\Model\ProductSaleElements;
use Thelia\TaxEngine\TaxEngine;
/**
* Class CustomerFamilyPriceListener
* @package CustomerFamily\EventListeners
* @author Etienne Perriere <eperriere@openstudio.fr>
*/
class CustomerFamilyPriceListener implements EventSubscriberInterface
{
protected $securityContext;
protected $taxEngine;
protected $customerFamilyService;
public function __construct(SecurityContext $securityContext, TaxEngine $taxEngine, CustomerFamilyService $customerFamilyService)
{
$this->securityContext = $securityContext;
$this->taxEngine = $taxEngine;
$this->customerFamilyService = $customerFamilyService;
}
/**
* Returns an array of event names this subscriber wants to listen to.
*
* @return array The event names to listen to
*/
public static function getSubscribedEvents()
{
return [
TheliaEvents::getLoopExtendsEvent(TheliaEvents::LOOP_EXTENDS_BUILD_MODEL_CRITERIA, 'product') => ['extendProductModelCriteria', 128],
TheliaEvents::getLoopExtendsEvent(TheliaEvents::LOOP_EXTENDS_PARSE_RESULTS, 'product') => ['extendProductParseResult', 128],
TheliaEvents::getLoopExtendsEvent(TheliaEvents::LOOP_EXTENDS_BUILD_MODEL_CRITERIA, 'product_sale_elements') => ['extendProductModelCriteria', 128],
TheliaEvents::getLoopExtendsEvent(TheliaEvents::LOOP_EXTENDS_PARSE_RESULTS, 'product_sale_elements') => ['extendProductParseResult', 128]
];
}
/**
* @param LoopExtendsBuildModelCriteriaEvent $event
* @throws \Propel\Runtime\Exception\PropelException
*/
public function extendProductModelCriteria(LoopExtendsBuildModelCriteriaEvent $event)
{
// Get customer's family
if (null !== $customerFamilyId = $this->customerFamilyService->getCustomerCustomerFamilyId()) {
// Get associated prices
$customerFamilyPrice = $this->customerFamilyService->getCustomerFamilyPrice($customerFamilyId, 0, 1);
$customerFamilyPromoPrice = $this->customerFamilyService->getCustomerFamilyPrice($customerFamilyId, 1, 1);
$useProductPrice = CustomerFamily::getConfigValue('customer_family_price_mode', null);
if ($customerFamilyPrice !== null || $customerFamilyPromoPrice !== null) {
// Get currency & search
$currencyId = Currency::getDefaultCurrency()->getId();
$search = $event->getModelCriteria();
// If $search is a ProductQuery, table alias is 'pse'
// Else $search is a ProductSaleElementsQuery ans there is no table alias
if ($search instanceof ProductQuery) {
$searchType = 'pse';
} else {
$searchType = null;
}
$tableName = ProductPurchasePriceTableMap::TABLE_NAME;
$colCurrencyId = ProductPurchasePriceTableMap::CURRENCY_ID;
$colPrice = ProductPurchasePriceTableMap::PURCHASE_PRICE;
if ($useProductPrice){
$tableName = ProductPriceTableMap::TABLE_NAME;
$colCurrencyId = ProductPriceTableMap::COL_CURRENCY_ID;
$colPrice = ProductPriceTableMap::COL_PRICE;
}
// Link each PSE with its corresponding purchase price, according to the PSE id
$productPurchasePriceJoin = new Join();
$productPurchasePriceJoin->addExplicitCondition(
ProductSaleElementsTableMap::TABLE_NAME,
'ID',
$searchType,
$tableName,
'PRODUCT_SALE_ELEMENTS_ID'
);
$productPurchasePriceJoin->setJoinType(Criteria::LEFT_JOIN);
// Add the link to the search, and add a link condition based on the currency
$search
->addJoinObject($productPurchasePriceJoin, 'purchase_price_join')
->addJoinCondition('purchase_price_join', $colCurrencyId.' = ?', $currencyId, null, \PDO::PARAM_INT);
// Add
$this->addProductCalculatedPrice($customerFamilyPrice, $search, $colPrice);
$this->addProductCalculatedPromoPrice($customerFamilyPromoPrice, $search, $colPrice);
}
}
}
/**
* @param LoopExtendsParseResultsEvent $event
* @throws \Propel\Runtime\Exception\PropelException
*/
public function extendProductParseResult(LoopExtendsParseResultsEvent $event)
{
// Get customer's family
if (null !== $customerFamilyId = $this->customerFamilyService->getCustomerCustomerFamilyId()) {
// Get associated prices
$customerFamilyPrice = $this->customerFamilyService->getCustomerFamilyPrice($customerFamilyId, 0, 1);
$customerFamilyPromoPrice = $this->customerFamilyService->getCustomerFamilyPrice($customerFamilyId, 1, 1);
if ($customerFamilyPrice !== null || $customerFamilyPromoPrice !== null) {
// Get loop result, tax country & security context
$loopResult = $event->getLoopResult();
$taxCountry = $this->taxEngine->getDeliveryCountry();
$securityContext = $this->securityContext;
foreach ($loopResult as $loopResultRow) {
/** @var \Thelia\Model\Product | \Thelia\Model\ProductSaleElements $product */
$product = $loopResultRow->model;
$customerFamilyPriceVirtualColumn = $product->getVirtualColumn('CUSTOMER_FAMILY_PRICE');
$customerFamilyPromoPriceVirtualColumn = $product->getVirtualColumn('CUSTOMER_FAMILY_PROMO_PRICE');
if (!empty($customerFamilyPriceVirtualColumn) || !empty($customerFamilyPromoPriceVirtualColumn)) {
$this->changeProductPrice(
$product,
$loopResultRow,
$taxCountry,
$securityContext
);
}
}
}
}
}
/********************************/
/**
* @param \CustomerFamily\Model\CustomerFamilyPrice $customerFamilyPrice
* @param \Propel\Runtime\ActiveQuery\ModelCriteria $search
* @param $colPrice
*/
protected function addProductCalculatedPrice($customerFamilyPrice, $search, $colPrice)
{
// Check if products' prices have to be changed depending on the customer's family
if ($customerFamilyPrice !== null) {
$search
->withColumn(
'IF (' . $colPrice . ' IS NULL,
NULL,
(' .
$colPrice .
'+' . $customerFamilyPrice->getAmountAddedBefore() .
') * ' . $customerFamilyPrice->getMultiplicationCoefficient() .
' + ' . $customerFamilyPrice->getAmountAddedAfter() .
')',
'CUSTOMER_FAMILY_PRICE'
);
} else {
$search->withColumn('NULL', 'CUSTOMER_FAMILY_PRICE');
}
}
/**
* @param \CustomerFamily\Model\CustomerFamilyPrice $customerFamilyPromoPrice
* @param \Propel\Runtime\ActiveQuery\ModelCriteria $search
* @param $colPrice
*/
protected function addProductCalculatedPromoPrice($customerFamilyPromoPrice, $search, $colPrice)
{
// Check if products' promo prices have to be changed depending on the customer's family
if ($customerFamilyPromoPrice !== null) {
$search
->withColumn(
'IF (' . $colPrice . ' IS NULL,
NULL,
(' .
$colPrice .
'+' . $customerFamilyPromoPrice->getAmountAddedBefore() .
') * ' . $customerFamilyPromoPrice->getMultiplicationCoefficient() .
' + ' . $customerFamilyPromoPrice->getAmountAddedAfter() .
')',
'CUSTOMER_FAMILY_PROMO_PRICE'
);
} else {
$search->withColumn('NULL', 'CUSTOMER_FAMILY_PROMO_PRICE');
}
}
/********************************/
/**
* @param \Thelia\Model\Product | \Thelia\Model\ProductSaleElements $product
* @param \Thelia\Core\Template\Element\LoopResultRow $loopResultRow
* @param \Thelia\Model\Country $taxCountry
* @param SecurityContext $securityContext
*/
protected function changeProductPrice(
$product,
$loopResultRow,
$taxCountry,
SecurityContext $securityContext
) {
$price = $loopResultRow->get('PRICE');
$priceTax = $loopResultRow->get('PRICE_TAX');
$taxedPrice = $loopResultRow->get('TAXED_PRICE');
$promoPrice = $loopResultRow->get('PROMO_PRICE');
$promoPriceTax = $loopResultRow->get('PROMO_PRICE_TAX');
$taxedPromoPrice = $loopResultRow->get('TAXED_PROMO_PRICE');
// Replace price
$customerFamilyPriceVirtualColumn = $product->getVirtualColumn('CUSTOMER_FAMILY_PRICE');
if (!empty($customerFamilyPriceVirtualColumn)) {
$price = round($product->getVirtualColumn('CUSTOMER_FAMILY_PRICE'), 2);
// If the customer has permanent discount, apply it
if ($securityContext->hasCustomerUser() && $securityContext->getCustomerUser()->getDiscount() > 0) {
$price = $price * (1 - ($securityContext->getCustomerUser()->getDiscount() / 100));
}
// Tax price
try {
// If $product is a Product, getTaxedPrice() takes a Country and a price as arguments
// Else if $product is a ProductSaleElements, getTaxedPrice() takes a Country and the price virtual column name as arguments
if ($product instanceof Product) {
$taxedPrice = $product->getTaxedPrice($taxCountry, $price);
} elseif ($product instanceof ProductSaleElements) {
$taxedPrice = $product->getTaxedPrice($taxCountry, 'CUSTOMER_FAMILY_PRICE');
}
} catch (TaxEngineException $e) {
$taxedPrice = null;
}
$priceTax = $taxedPrice - $price;
// Set new price & tax into the loop
$loopResultRow
->set("PRICE", $price)
->set("PRICE_TAX", $priceTax)
->set("TAXED_PRICE", $taxedPrice);
}
// Replace promo price
$customerFamilyPromoPriceVirtualColumn = $product->getVirtualColumn('CUSTOMER_FAMILY_PROMO_PRICE');
if (!empty($customerFamilyPromoPriceVirtualColumn)) {
$promoPrice = round($product->getVirtualColumn('CUSTOMER_FAMILY_PROMO_PRICE'), 2);
// If the customer has permanent discount, apply it
if ($securityContext->hasCustomerUser() && $securityContext->getCustomerUser()->getDiscount() > 0) {
$promoPrice = $promoPrice * (1 - ($securityContext->getCustomerUser()->getDiscount() / 100));
}
// Tax promo price
try {
// If $product is a Product, getTaxedPrice() takes a Country and a price as arguments
// Else if $product is a ProductSaleElements, getTaxedPrice() takes a Country and the price virtual column name as arguments
if ($product instanceof Product) {
$taxedPromoPrice = $product->getTaxedPromoPrice($taxCountry, $promoPrice);
} elseif ($product instanceof ProductSaleElements) {
$taxedPromoPrice = $product->getTaxedPromoPrice($taxCountry, 'CUSTOMER_FAMILY_PROMO_PRICE');
}
} catch (TaxEngineException $e) {
$taxedPromoPrice = null;
}
$promoPriceTax = $taxedPromoPrice - $promoPrice;
// Set new promo price & tax into the loop
$loopResultRow
->set("PROMO_PRICE", $promoPrice)
->set("PROMO_PRICE_TAX", $promoPriceTax)
->set("TAXED_PROMO_PRICE", $taxedPromoPrice);
}
// If current row is a product
if ($product instanceof Product) {
$loopResultRow
->set("BEST_PRICE", $product->getVirtualColumn('is_promo') ? $promoPrice : $price)
->set("BEST_PRICE_TAX", $product->getVirtualColumn('is_promo') ? $promoPriceTax : $priceTax)
->set("BEST_TAXED_PRICE", $product->getVirtualColumn('is_promo') ? $taxedPromoPrice : $taxedPrice);
}
}
}

View File

@@ -0,0 +1,97 @@
<?php
namespace CustomerFamily\EventListeners;
use CustomerFamily\CustomerFamily;
use CustomerFamily\Model\ProductPurchasePrice;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Validator\Constraints;
use Thelia\Core\Event\Product\ProductCreateEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Event\TheliaFormEvent;
use Thelia\Core\HttpFoundation\Request;
use Thelia\Core\Translation\Translator;
/**
* Class ProductCreationFormListener
* @package CustomerFamily\EventListeners
* @author Etienne Perriere <eperriere@openstudio.fr>
*/
class ProductCreationFormListener implements EventSubscriberInterface
{
/** @var \Thelia\Core\HttpFoundation\Request */
protected $request;
/**
* @param Request $request
*/
public function __construct(Request $request)
{
$this->request = $request;
}
/**
* Returns an array of event names this subscriber wants to listen to.
*
* @return array The event names to listen to
*/
public static function getSubscribedEvents()
{
return [
TheliaEvents::FORM_AFTER_BUILD.'.thelia_product_creation' => ['addPurchasePriceOnProductCreation', 128],
TheliaEvents::PRODUCT_CREATE => ['createProductPurchasePrice', 96]
];
}
/**
* Add purchase price input to product creation form
*
* @param TheliaFormEvent $event
*/
public function addPurchasePriceOnProductCreation(TheliaFormEvent $event)
{
$event->getForm()->getFormBuilder()
->add(
'purchase_price',
'number',
[
'constraints' => [
new Constraints\GreaterThanOrEqual(['value' => 0])
],
'label' => $this->trans('Purchase price'),
'label_attr' => ['for' => 'purchase_price']
]
)
;
}
/**
* Create purchase price when product is created
*
* @param ProductCreateEvent $event
* @throws \Exception
* @throws \Propel\Runtime\Exception\PropelException
*/
public function createProductPurchasePrice(ProductCreateEvent $event)
{
if (null != $purchasePrice = $this->request->get('thelia_product_creation')['purchase_price']) {
(new ProductPurchasePrice())
->setProductSaleElementsId($event->getProduct()->getDefaultSaleElements()->getId())
->setCurrencyId($event->getCurrencyId())
->setPurchasePrice($purchasePrice)
->save()
;
}
}
/**
* Utility for translations
* @param $id
* @param array $parameters
* @return string
*/
protected static function trans($id, array $parameters = array())
{
return Translator::getInstance()->trans($id, $parameters, CustomerFamily::MESSAGE_DOMAIN);
}
}

View File

@@ -0,0 +1,87 @@
<?php
namespace CustomerFamily\EventListeners;
use CustomerFamily\Model\ProductPurchasePrice;
use CustomerFamily\Model\ProductPurchasePriceQuery;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Validator\Constraints;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Event\TheliaFormEvent;
/**
* Class ProductExtendPriceFormListener
* @package CustomerFamily\EventListeners
* @author Etienne Perriere <eperriere@openstudio.fr>
*/
class ProductExtendPriceFormListener implements EventSubscriberInterface
{
/**
* Returns an array of event names this subscriber wants to listen to.
*
* @return array The event names to listen to
*/
public static function getSubscribedEvents()
{
return [TheliaEvents::FORM_AFTER_BUILD . '.thelia_product_default_sale_element_update_form' => ['extendProductPriceForm', 128]];
}
/**
* Add a purchase price input to the product
*
* @param TheliaFormEvent $event
*/
public function extendProductPriceForm(TheliaFormEvent $event)
{
$event
->getForm()
->getFormBuilder()
->addEventListener(FormEvents::POST_SUBMIT, [$this, 'handleExtendedData'], 0);
$event->getForm()->getFormBuilder()
->add(
'purchase_price',
'number',
[
'constraints' => [
new Constraints\GreaterThanOrEqual(['value' => 0])
]
]
);
}
/**
* Create or update product's default PSE's purchase price
*
* @param FormEvent $formEvent
* @throws \Exception
* @throws \Propel\Runtime\Exception\PropelException
*/
public function handleExtendedData(FormEvent $formEvent)
{
if (!$formEvent->getForm()->isValid()) {
return;
}
$data = $formEvent->getData();
if (null !== $purchasePrice = ProductPurchasePriceQuery::create()
->filterByProductSaleElementsId($data['product_sale_element_id'])
->findOneByCurrencyId($data['currency'])) {
// Update purchase price
$purchasePrice
->setPurchasePrice($data['purchase_price'])
->save();
} else {
// Create new purchase price
(new ProductPurchasePrice())
->setProductSaleElementsId($data['product_sale_element_id'])
->setCurrencyId($data['currency'])
->setPurchasePrice($data['purchase_price'])
->save();
}
}
}

View File

@@ -0,0 +1,95 @@
<?php
namespace CustomerFamily\EventListeners;
use CustomerFamily\Model\ProductPurchasePrice;
use CustomerFamily\Model\ProductPurchasePriceQuery;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Validator\Constraints;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\Event\TheliaFormEvent;
/**
* Class PseExtendPriceFormListener
* @package CustomerFamily\EventListeners
* @author Etienne Perriere <eperriere@openstudio.fr>
*/
class PseExtendPriceFormListener implements EventSubscriberInterface
{
/**
* Returns an array of event names this subscriber wants to listen to.
*
* @return array The event names to listen to
*/
public static function getSubscribedEvents()
{
return [TheliaEvents::FORM_AFTER_BUILD . '.thelia_product_sale_element_update_form' => ['extendPsePriceForm', 128]];
}
/**
* Add a purchase price input to PSEs
*
* @param TheliaFormEvent $event
*/
public function extendPsePriceForm(TheliaFormEvent $event)
{
$event
->getForm()
->getFormBuilder()
->addEventListener(FormEvents::POST_SUBMIT, [$this, 'handleExtendedData'], 0);
$event->getForm()->getFormBuilder()
->add(
'purchase_price',
'collection',
[
'type' => 'number',
'allow_add' => true,
'allow_delete' => true,
'options' => [
'constraints' => [
new Constraints\GreaterThanOrEqual(['value' => 0])
]
]
]
);
}
/**
* Create or update PSE's purchase price
*
* @param FormEvent $formEvent
* @throws \Exception
* @throws \Propel\Runtime\Exception\PropelException
*/
public function handleExtendedData(FormEvent $formEvent)
{
if (!$formEvent->getForm()->isValid()) {
return;
}
$data = $formEvent->getData();
if (is_array($data['product_sale_element_id'])) {
foreach (array_keys($data['product_sale_element_id']) as $idx) {
if (null !== $purchasePrice = ProductPurchasePriceQuery::create()
->filterByProductSaleElementsId($data['product_sale_element_id'][$idx])
->findOneByCurrencyId($data['currency'])) {
// Update purchase price
$purchasePrice
->setPurchasePrice($data['purchase_price'][$idx])
->save();
} else {
// Create new purchase price
(new ProductPurchasePrice())
->setProductSaleElementsId($data['product_sale_element_id'][$idx])
->setCurrencyId($data['currency'])
->setPurchasePrice($data['purchase_price'][$idx])
->save();
}
}
}
}
}

View File

@@ -0,0 +1,122 @@
<?php
/*************************************************************************************/
/* This file is part of the module CustomerFamily */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace CustomerFamily\Form;
use Symfony\Component\Validator\Constraints;
use CustomerFamily\CustomerFamily;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Thelia\Core\Translation\Translator;
use Thelia\Form\BaseForm;
/**
* Class CustomerCustomerFamilyForm
* @package CustomerFamily\Form
*/
class CustomerCustomerFamilyForm extends BaseForm
{
/**
* @return string the name of you form. This name must be unique
*/
public function getName()
{
return 'customer_customer_family_form';
}
/**
* Validate a field only if customer family is professional
*
* @param string $value
* @param ExecutionContextInterface $context
*/
public function checkProfessionalInformation($value, ExecutionContextInterface $context)
{
$customerFamily = CustomerFamily::getCustomerFamilyByCode(CustomerFamily::CUSTOMER_FAMILY_PROFESSIONAL);
if (null != $form = $this->getRequest()->request->get("customer_customer_family_form")) {
if (array_key_exists("customer_family_id", $form) &&
$form["customer_family_id"] == $customerFamily->getId()) {
if (strlen($value) <= 1) {
$context->addViolation(Translator::getInstance()->trans(
"This field can't be empty",
array(),
CustomerFamily::MESSAGE_DOMAIN
));
}
}
}
}
protected function buildForm()
{
$this->formBuilder
->add('customer_id', 'integer', array(
'constraints' => array(
new Constraints\NotBlank()
),
'label' => Translator::getInstance()->trans(
'Customer',
array(),
CustomerFamily::MESSAGE_DOMAIN
),
'label_attr' => array(
'for' => 'customer_id'
)
))
->add('customer_family_id', 'integer', array(
'constraints' => array(
new Constraints\NotBlank()
),
'label' => Translator::getInstance()->trans(
'Customer family',
array(),
CustomerFamily::MESSAGE_DOMAIN
),
'label_attr' => array(
'for' => 'customer_id'
)
))
->add(
'siret',
'text',
array(
'required' => false,
'empty_data' => false,
'label' => Translator::getInstance()->trans(
'Siret number',
array(),
CustomerFamily::MESSAGE_DOMAIN
),
'label_attr' => array(
'for' => 'siret'
)
)
)
->add(
'vat',
'text',
array(
'required' => false,
'empty_data' => false,
'label' => Translator::getInstance()->trans(
'Vat',
array(),
CustomerFamily::MESSAGE_DOMAIN
),
'label_attr' => array(
'for' => 'vat'
)
)
)
;
}
}

View File

@@ -0,0 +1,110 @@
<?php
/*************************************************************************************/
/* This file is part of the module CustomerFamily */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace CustomerFamily\Form;
use CustomerFamily\CustomerFamily;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Thelia\Core\Translation\Translator;
use Thelia\Form\BaseForm;
use Symfony\Component\Validator\Constraints\Callback;
use Thelia\Model\LangQuery;
/**
* Class CustomerFamilyCreateForm
* @package CustomerFamily\Form
*/
class CustomerFamilyCreateForm extends BaseForm
{
/**
* @return string the name of you form. This name must be unique
*/
public function getName()
{
return 'customer_family_create_form';
}
/**
* @param $value
* @param ExecutionContextInterface $context
*/
public function checkLocale($value, ExecutionContextInterface $context)
{
if (!LangQuery::create()->findOneByCode($value) === null) {
$context->addViolation(Translator::getInstance()->trans(
"Invalid locale"
));
}
}
protected function buildForm()
{
$this->formBuilder
->add(
'code',
'text',
array(
'constraints' => array(
new NotBlank()
),
'required' => true,
'empty_data' => false,
'label' => Translator::getInstance()->trans(
'Code',
array(),
CustomerFamily::MESSAGE_DOMAIN
),
'label_attr' => array(
'for' => 'code'
)
)
)
->add(
'title',
'text',
array(
'constraints' => array(
new NotBlank()
),
'required' => true,
'empty_data' => false,
'label' => Translator::getInstance()->trans(
'Title'
),
'label_attr' => array(
'for' => 'title'
)
)
)
->add(
'locale',
'text',
array(
'constraints' => array(
new NotBlank(),
new Callback(array("methods" => array(
array($this, "checkLocale")
)))
),
'required' => true,
'empty_data' => false,
'label' => Translator::getInstance()->trans(
'Locale'
),
'label_attr' => array(
'for' => 'locale'
)
)
);
}
}

View File

@@ -0,0 +1,35 @@
<?php
/*************************************************************************************/
/* This file is part of the module CustomerFamily */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace CustomerFamily\Form;
use Thelia\Form\BaseForm;
/**
* Class CustomerFamilyDeleteForm
* @package CustomerFamily\Form
*/
class CustomerFamilyDeleteForm extends BaseForm
{
/**
* @return string the name of you form. This name must be unique
*/
public function getName()
{
return 'customer_family_delete_form';
}
protected function buildForm()
{
}
}

View File

@@ -0,0 +1,66 @@
<?php
namespace CustomerFamily\Form;
use Thelia\Form\BaseForm;
/**
* Class CustomerFamilyPriceForm
* @package CustomerFamily\Form
* @author Etienne Perriere <eperriere@openstudio.fr>
*/
class CustomerFamilyPriceForm extends BaseForm
{
public function getName()
{
return 'customer_family_price_update';
}
protected function buildForm()
{
$this->formBuilder
->add(
'customer_family_id',
'integer'
)
->add(
'promo',
'integer'
)
->add(
'use_equation',
'checkbox',
[]
)
->add(
'amount_added_before',
'number',
[
'precision' => 6,
'required' => false
]
)
->add(
'amount_added_after',
'number',
[
'precision' => 6,
'required' => false
]
)
->add(
'coefficient',
'number',
[
'precision' => 6,
'required' => false
]
)
->add(
'is_taxed',
'checkbox',
[]
)
;
}
}

View File

@@ -0,0 +1,45 @@
<?php
/**
* Created by PhpStorm.
* User: nicolasbarbey
* Date: 19/09/2019
* Time: 09:30
*/
namespace CustomerFamily\Form;
use CustomerFamily\CustomerFamily;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Thelia\Form\BaseForm;
class CustomerFamilyPriceModeForm extends BaseForm
{
public function getName()
{
return 'customer_family_price_mode';
}
protected function buildForm()
{
$value = CustomerFamily::getConfigValue("customer_family_price_mode", 0);
$value = $value == 1 ? true : false;
$this->formBuilder
->add(
"price_mode",
CheckboxType::class,
[
"label" => $this->translator->trans("Use the product price", [], CustomerFamily::MESSAGE_DOMAIN),
"data" => $value,
'label_attr' => [
'help' => $this->translator->trans(
"By checking this check box this module will use the product price of Thelia instead of the the purchase price of CustomerFamily",
[],
CustomerFamily::MESSAGE_DOMAIN),
]
]
);
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace CustomerFamily\Form;
use Thelia\Form\BaseForm;
/**
* Class CustomerFamilyUpdateDefaultForm
* @package CustomerFamily\Form
*/
class CustomerFamilyUpdateDefaultForm extends BaseForm
{
public function getName()
{
return 'customer_family_update_default_form';
}
protected function buildForm()
{
$this->formBuilder
->add(
'customer_family_id',
'integer'
);
}
}

View File

@@ -0,0 +1,33 @@
<?php
/*************************************************************************************/
/* This file is part of the module CustomerFamily */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace CustomerFamily\Form;
/**
* Class CustomerFamilyUpdateForm
* @package CustomerFamily\Form
*/
class CustomerFamilyUpdateForm extends CustomerFamilyCreateForm
{
/**
* @return string the name of you form. This name must be unique
*/
public function getName()
{
return 'customer_family_update_form';
}
protected function buildForm()
{
parent::buildForm();
}
}

View File

@@ -0,0 +1,53 @@
<?php
/*************************************************************************************/
/* This file is part of the module CustomerFamily */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace CustomerFamily\Hook;
use CustomerFamily\CustomerFamily;
use Thelia\Core\Event\Hook\HookRenderBlockEvent;
use Thelia\Core\Hook\BaseHook;
class CustomerFamilyAccountDisplayHook extends BaseHook
{
public function onAccountAdditional(HookRenderBlockEvent $event)
{
$customer = $this->getCustomer();
if (is_null($customer)) {
// No customer => nothing to do.
return;
}
$customerId = $customer->getId();
if ($customerId <= 0) {
// Wrong customer => return.
return;
}
$title = $this->trans('My customer family', [], CustomerFamily::MESSAGE_DOMAIN);
$event->add(array(
'id' => $customerId,
'title' => $title,
'content' => $this->render(
'account-additional.html',
array(
'customerId' => $customerId,
'messageDomain' => CustomerFamily::MESSAGE_DOMAIN,
'particular' => CustomerFamily::CUSTOMER_FAMILY_PARTICULAR,
'title' => $title,
)
),
));
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace CustomerFamily\Hook;
use Thelia\Core\Event\Hook\HookRenderEvent;
use Thelia\Core\Hook\BaseHook;
/**
* Class CustomerFamilyHook
* @package CustomerFamily\Hook
* @author Etienne Perriere <eperriere@openstudio.fr>
*/
class CustomerFamilyHook extends BaseHook
{
public function onAddCss(HookRenderEvent $event)
{
$event->add($this->addCSS('assets/css/style.css'));
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace CustomerFamily\Hook;
use Thelia\Core\Event\Hook\HookRenderEvent;
use Thelia\Core\Hook\BaseHook;
/**
* Class CustomerFamilyProductPriceHook
* @package CustomerFamily\Hook
* @author Etienne Perriere <eperriere@openstudio.fr>
*/
class CustomerFamilyProductPriceHook extends BaseHook
{
public function onPsePriceEdit(HookRenderEvent $event)
{
$event->add($this->render(
'product-edit-price.html',
[
'pseId' => $event->getArgument('pse'),
'idx' => $event->getArgument('idx')
]
));
}
}

View File

@@ -0,0 +1,44 @@
<?php
/*************************************************************************************/
/* This file is part of the module CustomerFamily */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace CustomerFamily\Hook;
use CustomerFamily\CustomerFamily;
use Thelia\Core\Event\Hook\HookRenderEvent;
use Thelia\Core\Hook\BaseHook;
class CustomerFamilyRegisterFormHook extends BaseHook
{
/**
* Extra form fields.
* @param HookRenderEvent $event
*/
public function onRegisterFormBottom(HookRenderEvent $event)
{
$event->add($this->render(
'register.html',
array(
'form' => $event->getArgument('form'),
'messageDomain' => CustomerFamily::MESSAGE_DOMAIN,
)
));
}
/**
* Javascript for extra form fields.
* @param HookRenderEvent $event
*/
public function onRegisterAfterJSInclude(HookRenderEvent $event)
{
$event->add($this->addJS('assets/js/register.js'));
}
}

View File

@@ -0,0 +1,29 @@
<?php
/**
* Created by PhpStorm.
* User: nicolasbarbey
* Date: 18/09/2019
* Time: 16:45
*/
namespace CustomerFamily\Hook;
use Thelia\Core\Event\Hook\HookRenderBlockEvent;
use Thelia\Core\Hook\BaseHook;
use Thelia\Tools\URL;
class CustomerFamilyToolsHook extends BaseHook
{
public function onMainTopMenuTools(HookRenderBlockEvent $event)
{
$event->add(
[
'id' => 'tools_menu_customer_family',
'class' => '',
'url' => URL::getInstance()->absoluteUrl('/admin/module/CustomerFamily'),
'title' => "Customer families"
]
);
}
}

View File

@@ -0,0 +1,36 @@
<?php
/*************************************************************************************/
/* This file is part of the module CustomerFamily */
/* */
/* Copyright (c) OpenStudio */
/* email : dev@thelia.net */
/* web : http://www.thelia.net */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace CustomerFamily\Hook;
use CustomerFamily\CustomerFamily;
use Thelia\Core\Event\Hook\HookRenderEvent;
use Thelia\Core\Hook\BaseHook;
class CustomerFamilyUpdateFormHook extends BaseHook
{
public function onAccountUpdateFormBottom(HookRenderEvent $event)
{
$event->add($this->render(
'account-update.html',
array(
'form' => $event->getArgument('form'),
'messageDomain' => CustomerFamily::MESSAGE_DOMAIN,
)
));
}
public function onAccountUpdateAfterJSInclude(HookRenderEvent $event)
{
$event->add($this->addJS('assets/js/update.js'));
}
}

View File

@@ -0,0 +1,54 @@
<?php
return array(
'(need specific integration)' => '(nécessite une intégration spécifique)',
'Action' => 'Action',
'Add' => 'Ajouter',
'An error occured' => 'Une erreur est survenue',
'Brand restrictions' => 'Restriction de marque',
'Calculated price' => 'Prix calculé',
'Calculated prices for:' => 'Prix calculé pour :',
'Calculated promo price' => 'Prix promo calculé',
'Cancel' => 'Annuler',
'Category restrictions' => 'Restriction de catégorie',
'Choose what brands will be available for this customer family' => 'Choisissez les marques disponibles pour cette famille de client',
'Choose what categories will be available for this customer family' => 'Choisissez les catégories disponibles pour cette famille de client',
'Code' => 'Code',
'Customer Family' => 'Familles de clients',
'Customer family' => 'Famille',
'Default' => 'Défault',
'Define if your prices are the products\' ones or if they are calculated by the equation with your parameters.' => 'Définit si les produits affichent leur prix ou un prix calculé à partir de leur prix d\'achat via l\'équation suivante.',
'Delete' => 'Supprimer',
'Do you really want to delete this customer family ?' => 'Voulez-vous vraiment supprimer cette famille de clients ?',
'Edit' => 'Éditer',
'Edit information in %lng' => 'Information d\'édition en %lng',
'Enable brand restriction for this customer family' => 'Activer la restriction de marque pour cette famille de client',
'Enable category restriction for this customer family' => 'Activer la restriction de catégorie pour cette famille de client',
'Enter here the product purchase price in %title' => 'Indiquez ici le prix d\'achat HT en %title',
'Equation: ( ( product_purchase_price + fix_amount_1 ) x factor ) + fix_amount_2' => 'Equation : ( ( prix_d_achat + montant_fixe_1 ) x facteur ) + montant_fixe_2',
'Factor' => 'Facteur',
'Families' => 'Familles',
'Family of this customer' => 'Famille de ce client',
'Fix amount 1' => 'Montant fixe 1',
'Fix amount 2' => 'Montant fixe 2',
'ID' => 'ID',
'No' => 'Non',
'No family' => 'Aucune famille',
'Not using the equation will display the fix product price to customers.' => 'Ne pas utiliser l\'équation affichera le prix fixe des produits.',
'Placeholder SIRET' => 'SIRET',
'Placeholder V.A.T.' => 'VAT',
'Placeholder siret' => 'SIRET',
'Placeholder vat' => 'Taxe sur la Valeur Ajoutée',
'Price for:' => 'Prix pour :',
'Prices definition' => 'Définition des prix',
'Promo' => 'Promo',
'Purchase price' => 'Prix d\'achat',
'Purchase price (w/o taxes)' => 'Prix d\'achat HT',
'Save' => 'Enregistrer',
'Select customer family' => 'Sélectionnez le type de compte',
'Show taxes' => 'Afficher le prix taxé',
'Title' => 'Nom',
'Update' => 'Mettre à jour',
'Use equation' => 'Utiliser l\'équation',
'Yes' => 'Oui',
);

View File

@@ -0,0 +1,21 @@
<?php
return array(
//C
"Customer family" => "Customer family",
//F
"Family of this customer" => "Customer family",
//S
"Siret number" => "Siret number",
//T
"This field can't be empty" => "This field can't be empty",
//V
"Vat" => "VAT",
//P
'Placeholder SIRET' => 'Siret numbe',
'Placeholder V.A.T.' => 'VAT',
);

Some files were not shown because too many files have changed in this diff Show More