Il manquait plein de fichiers dans Git

This commit is contained in:
2021-11-05 21:52:08 +01:00
parent ebb1376015
commit 50b55fba14
47 changed files with 3945 additions and 992 deletions

View File

@@ -0,0 +1,68 @@
<?php
/*************************************************************************************/
/* This file is part of the module AdminOrderCreation */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace AdminOrderCreation;
use Propel\Runtime\Connection\ConnectionInterface;
use Thelia\Model\ModuleQuery;
use Thelia\Module\BaseModule;
class AdminOrderCreation extends BaseModule
{
/** @var string */
const DOMAIN_NAME = 'adminordercreation';
const CONFIG_KEY_DEFAULT_NEW_CREDIT_NOTE_STATUS_ID = 'default-new-credit-note-status-id';
const CONFIG_KEY_DEFAULT_NEW_CREDIT_NOTE_TYPE_ID = 'default-new-credit-note-type-id';
const CONFIG_KEY_PAYED_ORDER_MINIMUM_STATUS_ID = 'payed-order-minimum-status-id';
const CONFIG_KEY_INVOICE_REF_TYPE = 'invoice-ref-type'; // 0 for default thelia, 1 for separate
const CONFIG_KEY_INVOICE_REF_INCREMENT = 'invoice-ref-increment';
const CONFIG_DEFAULT_VALUE_DEFAULT_NEW_CREDIT_NOTE_STATUS_ID = 4;
const CONFIG_DEFAULT_VALUE_DEFAULT_NEW_CREDIT_NOTE_TYPE_ID = 7;
const CONFIG_DEFAULT_VALUE_PAYED_ORDER_MINIMUM_STATUS_ID = 2;
const CONFIG_DEFAULT_VALUE_INVOICE_REF_TYPE = 0;
public function update($currentVersion, $newVersion, ConnectionInterface $con = null)
{
if (null === self::getConfigValue(self::CONFIG_KEY_DEFAULT_NEW_CREDIT_NOTE_STATUS_ID)) {
self::setConfigValue(
self::CONFIG_KEY_DEFAULT_NEW_CREDIT_NOTE_STATUS_ID,
self::CONFIG_DEFAULT_VALUE_DEFAULT_NEW_CREDIT_NOTE_STATUS_ID
);
}
if (null === self::getConfigValue(self::CONFIG_KEY_DEFAULT_NEW_CREDIT_NOTE_TYPE_ID)) {
self::setConfigValue(
self::CONFIG_KEY_DEFAULT_NEW_CREDIT_NOTE_TYPE_ID,
self::CONFIG_DEFAULT_VALUE_DEFAULT_NEW_CREDIT_NOTE_TYPE_ID
);
}
if (null === self::getConfigValue(self::CONFIG_KEY_PAYED_ORDER_MINIMUM_STATUS_ID)) {
self::setConfigValue(
self::CONFIG_KEY_PAYED_ORDER_MINIMUM_STATUS_ID,
self::CONFIG_DEFAULT_VALUE_PAYED_ORDER_MINIMUM_STATUS_ID
);
}
if (null === self::getConfigValue(self::CONFIG_KEY_INVOICE_REF_TYPE)) {
self::setConfigValue(
self::CONFIG_KEY_INVOICE_REF_TYPE,
self::CONFIG_DEFAULT_VALUE_INVOICE_REF_TYPE
);
}
if (null === self::getConfigValue(self::CONFIG_KEY_INVOICE_REF_INCREMENT)) {
self::setConfigValue(
self::CONFIG_KEY_INVOICE_REF_INCREMENT,
1
);
}
}
}

View File

@@ -0,0 +1,7 @@
#1.3.1
- OpenSource version <3
#1.2.0
- First public version

View File

@@ -0,0 +1,21 @@
<?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="admin-order-creation.create" class="AdminOrderCreation\Form\OrderCreateForm" />
</forms>
<hooks>
<hook id="adminordercreation.hook.back.order" class="AdminOrderCreation\Hook\Back\OrderHook">
<tag name="hook.event_listener" event="orders.table-header" type="back" method="onOrdersTableHeader" />
<tag name="hook.event_listener" event="orders.js" type="back" method="onOrderJs" />
</hook>
<hook id="adminordercreation.hook.back.order.edit" class="AdminOrderCreation\Hook\Back\OrderEditHook">
<tag name="hook.event_listener" event="order.edit-js" type="back" method="onOrderAddButtonJs" />
<tag name="hook.event_listener" event="order.edit-js" type="back" method="onOrderJs" />
</hook>
</hooks>
</config>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="http://thelia.net/schema/dic/module"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://thelia.net/schema/dic/module http://thelia.net/schema/dic/module/module-2_2.xsd">
<fullnamespace>AdminOrderCreation\AdminOrderCreation</fullnamespace>
<descriptive locale="en_US">
<title>Module for generate an order from the back office</title>
</descriptive>
<descriptive locale="fr_FR">
<title>Module pour générer des commandes depuis le back office</title>
</descriptive>
<languages>
<language>en_US</language>
<language>fr_FR</language>
</languages>
<version>1.3.4</version>
<authors>
<author>
<name>Gilles Bourgeat</name>
<email>gilles.bourgeat@gmail.com</email>
</author>
</authors>
<type>classic</type>
<thelia>2.3.0</thelia>
<stability>beta</stability>
<mandatory>0</mandatory>
<hidden>0</hidden>
</module>

View File

@@ -0,0 +1,18 @@
<?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="adminordercreation.ajax.modal.create" path="/admin/admin-order-creation/ajax/modal/create">
<default key="_controller">AdminOrderCreation:Order:ajaxModalCreate</default>
</route>
<route id="adminordercreation.ajax.search.customer" path="/admin/admin-order-creation/ajax/search/customer">
<default key="_controller">AdminOrderCreation:Order:ajaxSearchCustomer</default>
</route>
<route id="adminordercreation.ajax.search.product" path="/admin/admin-order-creation/ajax/search/product">
<default key="_controller">AdminOrderCreation:Order:ajaxSearchProduct</default>
</route>
</routes>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<database defaultIdMethod="native" name="thelia"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../vendor/propel/propel/resources/xsd/database.xsd" >
<!--
See propel documentation on http://propelorm.org for all information about schema file
<table name="product_rel" namespace="AdminOrderCreation\Model">
<column autoIncrement="true" name="id" primaryKey="true" required="true" type="INTEGER" />
<column defaultValue="0" name="visible" required="true" type="TINYINT" />
<column defaultValue="0" name="position" required="true" type="INTEGER" />
<column name="title" size="255" type="VARCHAR" />
<column name="description" type="CLOB" />
<column name="chapo" type="LONGVARCHAR" />
<column name="postscriptum" type="LONGVARCHAR" />
<foreign-key foreignTable="product" name="fk_product_id" onDelete="CASCADE" onUpdate="RESTRICT">
<reference foreign="id" local="product_id" />
</foreign-key>
<behavior name="timestampable" />
<behavior name="i18n">
<parameter name="i18n_columns" value="title, description, chapo, postscriptum" />
</behavior>
<behavior name="versionable">
<parameter name="log_created_at" value="true" />
<parameter name="log_created_by" value="true" />
</behavior>
</table>
-->
<external-schema filename="local/config/schema.xml" referenceOnly="true" />
</database>

View File

@@ -0,0 +1,803 @@
<?php
/*************************************************************************************/
/* This file is part of the module AdminOrderCreation */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace AdminOrderCreation\Controller;
use AdminOrderCreation\AdminOrderCreation;
use AdminOrderCreation\Util\Calc;
use AdminOrderCreation\Util\CriteriaSearchTrait;
use CreditNote\Model\CreditNote;
use CreditNote\Model\CreditNoteAddress;
use CreditNote\Model\CreditNoteDetail;
use CreditNote\Model\CreditNoteQuery;
use CreditNote\Model\CreditNoteStatusQuery;
use CreditNote\Model\CreditNoteTypeQuery;
use CreditNote\Model\OrderCreditNote;
use InvoiceRef\EventListeners\OrderListener;
use Propel\Runtime\ActiveRecord\ActiveRecordInterface;
use Propel\Runtime\Propel;
use Symfony\Component\Form\FormError;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Thelia\Controller\Admin\BaseAdminController;
use Thelia\Core\Event\Order\OrderEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\HttpFoundation\Request;
use Thelia\Core\Security\AccessManager;
use Thelia\Core\Security\Resource\AdminResources;
use Thelia\Core\Template\Loop\ProductSaleElements;
use Thelia\Model\AddressQuery;
use Thelia\Model\Cart;
use Thelia\Model\CountryQuery;
use Thelia\Model\CurrencyQuery;
use Thelia\Model\Customer;
use Thelia\Model\CustomerQuery;
use Thelia\Model\Map\AddressTableMap;
use Thelia\Model\Map\OrderTableMap;
use Thelia\Model\Map\ProductI18nTableMap;
use AdminOrderCreation\Model\Order;
use Symfony\Component\Form\Form;
use Thelia\Model\OrderAddress;
use Thelia\Model\OrderProduct;
use Thelia\Model\OrderProductAttributeCombination;
use Thelia\Model\OrderProductTax;
use Thelia\Model\OrderStatusQuery;
use Thelia\Model\Product;
use Thelia\Model\ProductI18n;
use Thelia\Model\ProductQuery;
use Thelia\Model\ProductSaleElementsQuery;
use Thelia\Model\TaxRuleI18n;
use Thelia\Tools\I18n;
use Thelia\Tools\URL;
class OrderController extends BaseAdminController
{
use CriteriaSearchTrait;
public function ajaxModalCreateAction(Request $request)
{
if (null !== $response = $this->checkAuth(AdminResources::ORDER, [], AccessManager::CREATE)) {
return $response;
}
$order = new Order();
$order->setLang($this->getLang());
$order->setDispatcher($this->getDispatcher());
$form = $this->createForm('admin-order-creation.create', 'form', [], ['csrf_protection' => false]);
$formValidate = $this->validateForm($form, 'post');
$this->performOrder($order, $formValidate);
$this->getParserContext()->addForm($form);
$errorMessage = [];
foreach ($formValidate->getErrors() as $error) {
$errorMessage[] = $error->getMessage();
}
if (count($errorMessage)) {
$form->setErrorMessage(implode('<br/>', $errorMessage));
}
if (!$form->hasError() && 'create' === $formValidate->get('action')->getData()) {
$con = Propel::getServiceContainer()->getWriteConnection(OrderTableMap::DATABASE_NAME);
// use transaction because $criteria could contain info
// for more than one table (I guess, conceivably)
$con->beginTransaction();
try {
$cart = new Cart();
$cart->save();
$order->setCartId($cart->getId());
// on passe par le statut par défault
$order->setOrderStatus(
OrderStatusQuery::create()->findOneById(1)
);
$order->save();
$this->performCreditNote($order, $formValidate);
$orderStatusId = $formValidate->get('status_id')->getData();
if ((int) $orderStatusId >= 2) {
if ((int) AdminOrderCreation::getConfigValue(AdminOrderCreation::CONFIG_KEY_INVOICE_REF_TYPE) === 0) {
// pour retirer les stocks et générer la référence facture
$order->setOrderStatus(
OrderStatusQuery::create()->findOneById(2)
);
$this->getDispatcher()->dispatch(
TheliaEvents::ORDER_UPDATE_STATUS,
(new OrderEvent($order))->setStatus(2)
);
$order->save();
} else { // dans le cas d'une facturation à par
$order->setInvoiceRef((int) AdminOrderCreation::getConfigValue(AdminOrderCreation::CONFIG_KEY_INVOICE_REF_INCREMENT));
AdminOrderCreation::setConfigValue(
AdminOrderCreation::CONFIG_KEY_INVOICE_REF_INCREMENT,
(int) AdminOrderCreation::getConfigValue(AdminOrderCreation::CONFIG_KEY_INVOICE_REF_INCREMENT) + 1
);
$order->setOrderStatus(
OrderStatusQuery::create()->findOneById(2)
);
$order->save();
}
}
if ((int) $orderStatusId > 2) {
$order->setOrderStatus(
OrderStatusQuery::create()->findOneById((int) $orderStatusId)
);
$this->getDispatcher()->dispatch(
TheliaEvents::ORDER_UPDATE_STATUS,
(new OrderEvent($order))->setStatus((int) $orderStatusId)
);
}
$order->save();
$con->commit();
} catch (\Exception $e) {
$con->rollBack();
throw $e;
}
}
if ($order->getId()) {
return $this->render('admin-order-creation/ajax/order-create-modal-success', [
'order' => $order
]);
} else {
return $this->render('admin-order-creation/ajax/order-create-modal', [
'order' => $order,
'hasCreditNoteModule' => $this->hasCreditNoteModule(),
'configNewCreditNoteStatusId' => AdminOrderCreation::getConfigValue(AdminOrderCreation::CONFIG_KEY_DEFAULT_NEW_CREDIT_NOTE_STATUS_ID),
'configNewCreditNoteTypeId' => AdminOrderCreation::getConfigValue(AdminOrderCreation::CONFIG_KEY_DEFAULT_NEW_CREDIT_NOTE_TYPE_ID),
'configPayedOrderMinimumStatusId' => AdminOrderCreation::getConfigValue(AdminOrderCreation::CONFIG_KEY_PAYED_ORDER_MINIMUM_STATUS_ID)
]);
}
}
/**
* @param Request $request
* @return JsonResponse
* @throws \Propel\Runtime\Exception\PropelException
*/
public function ajaxSearchCustomerAction(Request $request)
{
if (null !== $response = $this->checkAuth(AdminResources::ORDER, [], AccessManager::CREATE)) {
return $response;
}
$customerQuery = CustomerQuery::create()
->innerJoinAddress()
->groupById()
->limit(20);
$this->whereConcatRegex($customerQuery, [
'customer.REF',
'customer.FIRSTNAME',
'customer.LASTNAME',
'customer.EMAIL',
'address.COMPANY',
'address.PHONE'
], $request->get('q'));
$customerQuery
->withColumn(AddressTableMap::COMPANY, 'COMPANY')
->withColumn(AddressTableMap::ADDRESS1, 'ADDRESS')
->withColumn(AddressTableMap::CITY, 'CITY')
->withColumn(AddressTableMap::ZIPCODE, 'ZIPCODE')
->withColumn(AddressTableMap::PHONE, 'PHONE');
$customers = $customerQuery->find();
$json = [
'incomplete_results' => count($customers) ? false : true,
'items' => []
];
/** @var Customer $customer */
foreach ($customers as $customer) {
$json['items'][] = [
'id' => $customer->getId(),
'company' => $customer->getVirtualColumn('COMPANY'),
'firstname' => $customer->getFirstname(),
'lastname' => $customer->getLastname(),
'ref' => $customer->getRef(),
'address' => $this->formatAddress($customer)
];
}
return new JsonResponse($json);
}
/**
* @param Request $request
* @return JsonResponse
* @throws \Propel\Runtime\Exception\PropelException
*/
public function ajaxSearchProductAction(Request $request)
{
if (null !== $response = $this->checkAuth(AdminResources::ORDER, [], AccessManager::CREATE)) {
return $response;
}
$productQuery = ProductQuery::create();
$productQuery->useI18nQuery(
$this->getRequest()->getSession()->getAdminEditionLang()->getLocale()
);
$productQuery
->withColumn(ProductI18nTableMap::TITLE, 'TITLE');
$this->whereConcatRegex($productQuery, array(
'product.REF',
'product_i18n.TITLE'
), $request->get('q'));
$productQuery->setLimit(10);
$products = $productQuery->find();
$json = [
'incomplete_results' => count($products) ? false : true,
'items' => []
];
/** @var Product $product */
foreach ($products as $product) {
$json['items'][] = [
'id' => $product->getId(),
'ref' => $product->getRef(),
'title' => $product->getVirtualColumn('TITLE')
];
}
return new JsonResponse($json);
}
protected function hasCreditNoteModule()
{
return class_exists('\CreditNote\CreditNote');
}
protected function performOrder(Order $order, Form $formValidate)
{
$this
->performCurrency($order, $formValidate)
->performOrderStatus($order, $formValidate)
->performCustomer($order, $formValidate)
->performInvoiceAddress($order, $formValidate)
->performDeliveryAddress($order, $formValidate)
->performDeliveryAddress($order, $formValidate)
->performProducts($order, $formValidate)
->performShipping($order, $formValidate)
->performGlobalReduction($order, $formValidate)
->performPaymentModule($order, $formValidate)
->performDeliveryModule($order, $formValidate)
;
return $this;
}
protected function performPaymentModule(Order $order, Form $form)
{
$paymentModuleId = (int) $form->get('payment_module_id')->getData();
$order->setPaymentModuleId($paymentModuleId);
return $this;
}
protected function performDeliveryModule(Order $order, Form $form)
{
$deliveryModuleId = (int) $form->get('delivery_module_id')->getData();
$order->setDeliveryModuleId($deliveryModuleId);
return $this;
}
protected function performShipping(Order $order, Form $form)
{
$price = (float) $form->get('shipping_price_with_tax')->getData();
$priceWithTax = (float) $form->get('shipping_price_with_tax')->getData();
$order->setPostage($priceWithTax);
$order->setPostageTax($priceWithTax - $price);
return $this;
}
protected function performGlobalReduction(Order $order, Form $form)
{
$reduction = (float) $form->get('reduction')->getData();
$reductionType = (int) $form->get('reduction_type')->getData();
$total = $order->getTotalAmountWithTax(false);
$afterDiscount = Calc::reduction(
$reduction,
$reductionType,
$total,
1
);
$order->setDiscount(-($afterDiscount - $total));
return $this;
}
protected function performCurrency(Order $order, Form $form)
{
/** @var int $currencyId */
$currencyId = $form->get('currency_id')->getData();
if (empty($currencyId) || null === $currency = CurrencyQuery::create()->findPk($currencyId)) {
$currency = CurrencyQuery::create()->findOneByByDefault(true);
}
$order->setCurrency($currency);
$order->setCurrencyRate($currency->getRate());
return $this;
}
protected function getCountry(Form $form)
{
/** @var int $countryId */
$countryId = $form->get('country_id')->getData();
if (!empty($countryId) && $country = CountryQuery::create()->findPk($countryId)) {
return $country;
}
return CountryQuery::create()->filterByByDefault(true)->findOne();
}
protected function performOrderStatus(Order $order, Form $form)
{
if (null !== $statusId = $form->get('status_id')->getData()) {
$order->setOrderStatus(
OrderStatusQuery::create()->findOneById((int) $statusId)
);
} else {
$order->setOrderStatus(
OrderStatusQuery::create()->findOneById(1)
);
}
return $this;
}
protected function performCustomer(Order $order, Form $form)
{
if (null !== $customerId = $form->get('customer_id')->getData()) {
$order->setCustomer(
CustomerQuery::create()->findOneById((int) $customerId)
);
}
if (null === $customerId && 'create' === $form->get('action')->getData()) {
$form->addError(
new FormError('Please select a customer')
);
}
return $this;
}
protected function performCreditNote(Order $order, Form $form)
{
if (!$this->hasCreditNoteModule()) {
return $this;
}
$action = $form->get('action')->getData();
$creditNoteId = $form->get('credit_note_id')->getData();
$creditNoteStatusId = $form->get('credit_note_status_id')->getData();
$creditNoteTypeId = $form->get('credit_note_type_id')->getData();
if ($creditNoteId && $action === 'create') {
/** @var CreditNote $creditNote */
$creditNote = CreditNoteQuery::create()
->filterById($creditNoteId)
->useCreditNoteStatusQuery()
->filterByUsed(false)
->filterByInvoiced(true)
->endUse()
->findOne();
if (null === $creditNote) {
$form->addError(
new FormError('Please select a valid credit note')
);
}
$orderCreditNote = (new OrderCreditNote())
->setOrderId($order->getId())
->setCreditNoteId($creditNote->getId());
if (round($order->getTotalAmountWithTax() - $creditNote->getTotalPriceWithTax(), 2) > 0) { // cas crédit note plus petit
$orderCreditNote->setAmountPrice($creditNote->getTotalPriceWithTax());
} elseif (round($order->getTotalAmountWithTax() - $creditNote->getTotalPriceWithTax(), 2) < 0) { // cas crédit note plus grand
$orderCreditNote->setAmountPrice($order->getTotalAmountWithTax());
// copy address
$lastInvoiceAddress = $creditNote->getCreditNoteAddress();
$invoiceAddress = (new CreditNoteAddress())
->setFirstname($lastInvoiceAddress->getFirstname())
->setLastname($lastInvoiceAddress->getLastname())
->setCity($lastInvoiceAddress->getCity())
->setZipcode($lastInvoiceAddress->getZipcode())
->setAddress1($lastInvoiceAddress->getAddress1())
->setAddress2($lastInvoiceAddress->getAddress2())
->setAddress3($lastInvoiceAddress->getAddress3())
->setCustomerTitleId($lastInvoiceAddress->getCustomerTitleId())
->setCountryId($lastInvoiceAddress->getCountryId());
$invoiceAddress->save();
$crediNoteDetail = (new CreditNoteDetail())
->setTitle('Remboursement différence')
->setTaxRuleId(null)
->setPriceWithTax(-($order->getTotalAmountWithTax() - $creditNote->getTotalPriceWithTax()))
->setPrice(-($order->getTotalAmountWithTax() - $creditNote->getTotalPriceWithTax()))
->setType('other');
$newCreditNote = (new CreditNote())
->setCurrency($creditNote->getCurrency())
->setTypeId($creditNoteTypeId)
->setStatusId($creditNoteStatusId)
->setCreditNoteAddress($invoiceAddress)
->addCreditNoteDetail($crediNoteDetail)
->setTotalPrice(-($order->getTotalAmountWithTax() - $creditNote->getTotalPriceWithTax()))
->setTotalPriceWithTax(-($order->getTotalAmountWithTax() - $creditNote->getTotalPriceWithTax()))
->setCustomerId($creditNote->getCustomer()->getId())
->setParentId($creditNote->getId())
->setOrderId($order->getId());
$newCreditNote->setDispatcher($this->getDispatcher());
$newCreditNote->save();
} else { // cas crédit note égale
$orderCreditNote->setAmountPrice($creditNote->getTotalPriceWithTax());
}
$orderCreditNote->save();
$newStatus = CreditNoteStatusQuery::findNextCreditNoteUsedStatus($creditNote->getCreditNoteStatus());
$creditNote->setCreditNoteStatus($newStatus);
$creditNote->save();
}
}
protected function performInvoiceAddress(Order $order, Form $form)
{
$action = $form->get('action')->getData();
$invoiceAddressId = $form->get('invoice_address_id')->getData();
if ($invoiceAddressId) {
$address = AddressQuery::create()->findOneById((int) $invoiceAddressId);
$orderAddress = (new OrderAddress())
->setCustomerTitle($address->getCustomerTitle())
->setAddress1($address->getAddress1())
->setAddress2($address->getAddress2())
->setAddress3($address->getAddress3())
->setFirstname($address->getFirstname())
->setLastname($address->getLastname())
->setCity($address->getCity())
->setZipcode($address->getZipcode())
->setCompany($address->getCompany())
->setCountry($address->getCountry());
} else {
$invoiceAddressTitle = $form->get('invoice_address_title')->getData();
$invoiceAddressFirstname = $form->get('invoice_address_firstname')->getData();
$invoiceAddressLastname = $form->get('invoice_address_lastname')->getData();
$invoiceAddressCompany = $form->get('invoice_address_company')->getData();
$invoiceAddressAddress1 = $form->get('invoice_address_address1')->getData();
$invoiceAddressAddress2 = $form->get('invoice_address_address2')->getData();
$invoiceAddressZipcode = $form->get('invoice_address_zipcode')->getData();
$invoiceAddressCity = $form->get('invoice_address_city')->getData();
$invoiceAddressCountryId = $form->get('invoice_address_country_id')->getData();
$orderAddress = (new OrderAddress())
->setCustomerTitleId($invoiceAddressTitle)
->setAddress1($invoiceAddressAddress1)
->setAddress2($invoiceAddressAddress2)
->setFirstname($invoiceAddressFirstname)
->setLastname($invoiceAddressLastname)
->setCity($invoiceAddressCity)
->setZipcode($invoiceAddressZipcode)
->setCompany($invoiceAddressCompany)
->setCountry(
CountryQuery::create()->findOneById($invoiceAddressCountryId)
);
}
if (empty($orderAddress->getLastname()) && 'create' === $form->get('action')->getData()) {
$form->addError(
new FormError('Please select a invoice address')
);
}
if ($action === 'create') {
$orderAddress->save();
$order->setInvoiceOrderAddressId($orderAddress->getId());
}
return $this;
}
protected function performDeliveryAddress(Order $order, Form $form)
{
$action = $form->get('action')->getData();
$deliveryAddressId = $form->get('delivery_address_id')->getData();
if ($deliveryAddressId) {
$address = AddressQuery::create()->findOneById((int) $deliveryAddressId);
$orderAddress = (new OrderAddress())
->setCustomerTitle($address->getCustomerTitle())
->setAddress1($address->getAddress1())
->setAddress2($address->getAddress2())
->setAddress3($address->getAddress3())
->setFirstname($address->getFirstname())
->setLastname($address->getLastname())
->setCity($address->getCity())
->setZipcode($address->getZipcode())
->setCompany($address->getCompany())
->setCountry($address->getCountry());
} else {
$deliveryAddressTitle = $form->get('delivery_address_title')->getData();
$deliveryAddressFirstname = $form->get('delivery_address_firstname')->getData();
$deliveryAddressLastname = $form->get('delivery_address_lastname')->getData();
$deliveryAddressCompany = $form->get('delivery_address_company')->getData();
$deliveryAddressAddress1 = $form->get('delivery_address_address1')->getData();
$deliveryAddressAddress2 = $form->get('delivery_address_address2')->getData();
$deliveryAddressZipcode = $form->get('delivery_address_zipcode')->getData();
$deliveryAddressCity = $form->get('delivery_address_city')->getData();
$deliveryAddressCountryId = $form->get('delivery_address_country_id')->getData();
$orderAddress = (new OrderAddress())
->setCustomerTitleId($deliveryAddressTitle)
->setAddress1($deliveryAddressAddress1)
->setAddress2($deliveryAddressAddress2)
->setFirstname($deliveryAddressFirstname)
->setLastname($deliveryAddressLastname)
->setCity($deliveryAddressCity)
->setZipcode($deliveryAddressZipcode)
->setCompany($deliveryAddressCompany)
->setCountry(
CountryQuery::create()->findOneById($deliveryAddressCountryId)
);
}
if (empty($orderAddress->getLastname()) && 'create' === $form->get('action')->getData()) {
$form->addError(
new FormError('Please select a delivery address')
);
}
if ($action === 'create') {
$orderAddress->save();
$order->setDeliveryOrderAddressId($orderAddress->getId());
}
return $this;
}
protected function getLang()
{
return $this->getSession()->getAdminEditionLang();
}
protected function performProducts(Order $order, Form $form)
{
$country = $this->getCountry($form);
$productIds = $form->get('product_id')->getData();
$quantities = $form->get('product_quantity')->getData();
$productSaleElementIds = $form->get('product_sale_element_id')->getData();
$productPriceWithoutTax = $form->get('product_price_without_tax')->getData();
$refreshPrice = $form->get('refresh_price')->getData();
$currency = $this->getCurrency($form);
foreach ($productIds as $key => $id) {
if (!isset($quantities[$key])) {
$quantities[$key] = 1;
}
$product = ProductQuery::create()->findOneById($id);
/** @var ProductI18n $productI18n */
$productI18n = I18n::forceI18nRetrieving(
$order->getLang()->getLocale(),
'Product',
$product->getId()
);
$productSaleElementsLoop = new ProductSaleElements($this->container);
if (isset($productSaleElementIds[$key])) {
if (null !== ProductSaleElementsQuery::create()
->filterByProductId($product->getId())
->filterById($productSaleElementIds[$key])
->findOne()) {
$productSaleElementsLoop->initializeArgs([
'name' => 'product_sale_elements',
'type' => 'product_sale_elements',
'id' => $productSaleElementIds[$key],
'currency' => $currency->getId()
]);
} else {
$productSaleElementsLoop->initializeArgs([
'name' => 'product_sale_elements',
'type' => 'product_sale_elements',
'product' => $product->getId(),
'currency' => $currency->getId()
]);
}
} else {
$productSaleElementsLoop->initializeArgs([
'name' => 'product_sale_elements',
'type' => 'product_sale_elements',
'product' => $product->getId(),
'currency' => $currency->getId()
]);
}
$pagination = null;
$results = $productSaleElementsLoop->exec($pagination);
/** @var \Thelia\Model\ProductSaleElements $productSaleElement */
$productSaleElement = $results->getResultDataCollection()[0];
/** @var TaxRuleI18n $taxRuleI18n */
$taxRuleI18n = I18n::forceI18nRetrieving(
$order->getLang()->getLocale(),
'TaxRule',
$product->getTaxRuleId()
);
if (isset($refreshPrice[$key]) && (int) $refreshPrice[$key]) {
$price = $productSaleElement->getVirtualColumn('price_PRICE');
$promoPrice = $productSaleElement->getVirtualColumn('price_PROMO_PRICE');
} else {
$price = isset($productPriceWithoutTax[$key]) ? (float) $productPriceWithoutTax[$key] : $productSaleElement->getVirtualColumn('price_PRICE');
$promoPrice = isset($productPriceWithoutTax[$key]) ? (float) $productPriceWithoutTax[$key] : $productSaleElement->getVirtualColumn('price_PROMO_PRICE');
}
$taxDetail = $product->getTaxRule()->getTaxDetail(
$product,
$country,
$price,
$promoPrice,
$order->getLang()->getLocale()
);
$orderProduct = (new OrderProduct())
->setProductRef($product->getRef())
->setProductSaleElementsRef($productSaleElement->getRef())
->setProductSaleElementsId($productSaleElement->getId())
->setTitle($productI18n->getTitle())
->setChapo($productI18n->getChapo())
->setDescription($productI18n->getDescription())
->setPostscriptum($productI18n->getPostscriptum())
->setVirtual($product->getVirtual())
->setQuantity($quantities[$key])
->setWasNew($productSaleElement->getNewness())
->setWeight($productSaleElement->getWeight())
->setTaxRuleTitle($taxRuleI18n->getTitle())
->setTaxRuleDescription($taxRuleI18n->getDescription())
->setEanCode($productSaleElement->getEanCode())
->setDispatcher($this->getDispatcher())
->setPrice($price)
->setPromoPrice($promoPrice)
->setWasInPromo($productSaleElement->getPromo())
;
/** @var OrderProductTax $tax */
foreach ($taxDetail as $tax) {
$orderProduct->addOrderProductTax($tax);
}
foreach ($productSaleElement->getAttributeCombinations() as $attributeCombination) {
/** @var \Thelia\Model\Attribute $attribute */
$attribute = I18n::forceI18nRetrieving(
$this->getSession()->getLang()->getLocale(),
'Attribute',
$attributeCombination->getAttributeId()
);
/** @var \Thelia\Model\AttributeAv $attributeAv */
$attributeAv = I18n::forceI18nRetrieving(
$this->getSession()->getLang()->getLocale(),
'AttributeAv',
$attributeCombination->getAttributeAvId()
);
$orderProduct->addOrderProductAttributeCombination(
(new OrderProductAttributeCombination())
->setOrderProductId($orderProduct->getId())
->setAttributeTitle($attribute->getTitle())
->setAttributeChapo($attribute->getChapo())
->setAttributeDescription($attribute->getDescription())
->setAttributePostscriptum($attribute->getPostscriptum())
->setAttributeAvTitle($attributeAv->getTitle())
->setAttributeAvChapo($attributeAv->getChapo())
->setAttributeAvDescription($attributeAv->getDescription())
->setAttributeAvPostscriptum($attributeAv->getPostscriptum())
);
}
$order->addOrderProduct($orderProduct);
}
if (!count($order->getOrderProducts()) && 'create' === $form->get('action')->getData()) {
$form->addError(
new FormError('Please select a product')
);
}
return $this;
}
protected function getCurrency(Form $form)
{
$currencyId = $form->get('currency_id')->getData();
if (null !== $currencyId) {
$currency = CurrencyQuery::create()->findPk($currencyId);
if (null === $currency) {
throw new \InvalidArgumentException('Cannot found currency id: `' . $currency . '` in product_sale_elements loop');
}
} else {
$currency = $this->getRequest()->getSession()->getCurrency();
}
return $currency;
}
/**
* @param ActiveRecordInterface $model
* @return string
* @throws \Propel\Runtime\Exception\PropelException
*/
protected function formatAddress(ActiveRecordInterface $model)
{
/** @var Order|Customer $model */
return implode(' ', [$model->getVirtualColumn('ADDRESS'), $model->getVirtualColumn('ZIPCODE'), $model->getVirtualColumn('CITY')]);
}
}

View File

@@ -0,0 +1,213 @@
<?php
/*************************************************************************************/
/* This file is part of the module AdminOrderCreation */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace AdminOrderCreation\Form;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Thelia\Form\BaseForm;
/**
* Class CreditNoteCreateForm
* @package CreditNote\Form
* @author Gilles Bourgeat <gilles.bourgeat@gmail.com>
*/
class OrderCreateForm extends BaseForm
{
/**
* @return string the name of you form. This name must be unique
*/
public function getName()
{
return 'admin-order-creation-create';
}
/**
*
* in this function you add all the fields you need for your Form.
* Form this you have to call add method on $this->formBuilder attribute :
*
*/
protected function buildForm()
{
$this->formBuilder
->add('currency_id', IntegerType::class, array(
'required' => false
))
->add('country_id', IntegerType::class, array(
'required' => false
));
$this->formBuilder
->add('action', ChoiceType::class, array(
'required' => true,
'choices' => array(
'open' => 'open',
'refresh' => 'refresh',
'create' => 'create',
),
))
->add('status_id', IntegerType::class, array(
'required' => false
))
->add('customer_id', IntegerType::class, array(
'required' => false
))
->add('invoice_address_id', IntegerType::class, array(
'required' => false
))
->add('delivery_address_id', IntegerType::class, array(
'required' => false
))
;
$this->formBuilder
->add('invoice_address_id', IntegerType::class, array(
'required' => false
))
->add('invoice_address_title', TextType::class, array(
'required' => false
))
->add('invoice_address_firstname', TextType::class, array(
'required' => false
))
->add('invoice_address_lastname', TextType::class, array(
'required' => false
))
->add('invoice_address_company', TextType::class, array(
'required' => false
))
->add('invoice_address_address1', TextType::class, array(
'required' => false
))
->add('invoice_address_address2', TextType::class, array(
'required' => false
))
->add('invoice_address_zipcode', TextType::class, array(
'required' => false
))
->add('invoice_address_city', TextType::class, array(
'required' => false
))
->add('invoice_address_country_id', IntegerType::class, array(
'required' => false
))
;
$this->formBuilder
->add('delivery_address_id', IntegerType::class, array(
'required' => false
))
->add('delivery_address_title', TextType::class, array(
'required' => false
))
->add('delivery_address_firstname', TextType::class, array(
'required' => false
))
->add('delivery_address_lastname', TextType::class, array(
'required' => false
))
->add('delivery_address_company', TextType::class, array(
'required' => false
))
->add('delivery_address_address1', TextType::class, array(
'required' => false
))
->add('delivery_address_address2', TextType::class, array(
'required' => false
))
->add('delivery_address_zipcode', TextType::class, array(
'required' => false
))
->add('delivery_address_city', TextType::class, array(
'required' => false
))
->add('delivery_address_country_id', IntegerType::class, array(
'required' => false
))
;
$this->formBuilder
->add('product_id', 'collection', array(
'required' => false,
'allow_add' => true,
'allow_delete' => true
))
->add('product_sale_element_id', 'collection', array(
'required' => false,
'allow_add' => true,
'allow_delete' => true
))
->add('product_quantity', 'collection', array(
'required' => false,
'allow_add' => true,
'allow_delete' => true
))
->add('product_price_with_tax', 'collection', array(
'required' => false,
'allow_add' => true,
'allow_delete' => true
))
->add('product_price_without_tax', 'collection', array(
'required' => false,
'allow_add' => true,
'allow_delete' => true
))
->add('refresh_price', 'collection', array(
'required' => false,
'allow_add' => true,
'allow_delete' => true
))
;
$this->formBuilder
->add('reduction', TextType::class, array(
'required' => false,
'empty_data' => 0
))
->add('reduction_type', NumberType::class, array(
'required' => false
));
$this->formBuilder
->add('shipping_price', TextType::class, array(
'required' => false,
'empty_data' => 0
))
->add('shipping_tax_rule_id', IntegerType::class, array(
'required' => false
))
->add('shipping_price_with_tax', TextType::class, array(
'required' => false,
'empty_data' => 0
));
$this->formBuilder
->add('payment_module_id', IntegerType::class, array(
'required' => false
))
->add('delivery_module_id', IntegerType::class, array(
'required' => false
))
;
$this->formBuilder
->add('credit_note_id', IntegerType::class, array(
'required' => false
))
->add('credit_note_type_id', IntegerType::class, array(
'required' => false
))
->add('credit_note_status_id', IntegerType::class, array(
'required' => false
))
;
}
}

View File

@@ -0,0 +1,22 @@
<?php
/*************************************************************************************/
/* This file is part of the module AdminOrderCreation */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace AdminOrderCreation\Hook\Back;
use Thelia\Core\Event\Hook\HookRenderEvent;
class OrderEditHook extends OrderHook
{
public function onOrderAddButtonJs(HookRenderEvent $event)
{
$event->add($this->render(
'admin-order-creation/hook/orders.edit.js.html',
$event->getArguments()
));
}
}

View File

@@ -0,0 +1,35 @@
<?php
/*************************************************************************************/
/* This file is part of the module AdminOrderCreation */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace AdminOrderCreation\Hook\Back;
use AdminOrderCreation\AdminOrderCreation;
use Thelia\Core\Event\Hook\HookRenderEvent;
use Thelia\Core\Hook\BaseHook;
use Thelia\Core\Thelia;
class OrderHook extends BaseHook
{
public function onOrdersTableHeader(HookRenderEvent $event)
{
$event->add($this->render(
'admin-order-creation/hook/orders.table-header.html',
$event->getArguments()
));
}
public function onOrderJs(HookRenderEvent $event)
{
$event->add($this->render(
'admin-order-creation/hook/orders.js.html',
array_merge($event->getArguments() + [
])
));
}
}

View File

@@ -0,0 +1,61 @@
<?php
return array(
'A new credit note of %amount with tax will be issued for this customer.' => 'Un nouvel avoir d\'un montant de %amount TTC sera créer pour le client.',
'Add product' => 'Ajouter un produit',
'Address :' => 'Adresse :',
'Address complement :' => 'Complément d\'adresse :',
'An error has occurred !!!' => 'Une erreur est survenue',
'Cancel' => 'Annuler',
'City :' => 'Ville :',
'Close' => 'Fermer',
'Company :' => 'Société :',
'Country :' => 'Pays :',
'Create' => 'Créer',
'Create new order' => 'Créer une nouvelle commande',
'Create order' => 'Créer une commande',
'Credit note :' => 'Avoir :',
'Customer :' => 'Client :',
'Default product sale element' => 'Déclinaison par défaut',
'Delete' => 'Supprimer',
'Delivery address' => 'Adresse de livraison',
'Delivery module :' => 'Module de livraison :',
'Firstname :' => 'Prénom :',
'Global amount' => 'Montant global',
'Global reduction' => 'Réduction global',
'Invoice address' => 'Adresse de facturation',
'Lastname :' => 'Nom :',
'Message content :' => 'Message :',
'Message status :' => 'Statut :',
'Order Informations :' => 'Informations de la commande',
'Other' => 'Autre',
'Payment module :' => 'Module de paiement :',
'Percentage' => 'Pourcentage',
'Please wait, loading' => 'Veillez patienter, chargement ...',
'Price' => 'Prix',
'Price with tax :' => 'Prix TTC :',
'Price without tax :' => 'Prix HT :',
'Product' => 'Produit',
'Product sale element' => 'Déclinaison',
'Products' => 'Produits',
'Quantity' => 'Quantité',
'Search...' => 'Rechercher ...',
'Shipping' => 'Frais de livraison',
'Status :' => 'Statut :',
'Status of new credit note :' => 'Statut du nouvel avoir :',
'Total tax :' => 'Total taxe :',
'Tax rule :' => 'Règle de taxe :',
'This command uses the credit note ref %ref for the amount of %amount with tax.' => 'Cette commande utilise l\'avoir %ref avec un montant de %amount TTC.',
'This order use the country : %country' => 'Cette commande utilise le pays : %country',
'This order use the currency : %currency' => 'Cette commande utilise le devise : %currency',
'Title :' => 'Titre :',
'Total with tax :' => 'Total TTC :',
'Total without tax :' => 'Total HT :',
'Type :' => 'Type :',
'Type of new credit note :' => 'Type du nouvel avoir :',
'Unit price with tax' => 'Prix unitaire TTC',
'Use Credit Note :' => 'Utiliser un avoir :',
'Value :' => 'Value :',
'Your customer will have to pay the difference of %amount with tax' => 'Votre client devra payer la différence d\'un montant de %amount TTC',
'Zipcode :' => 'Code postal :',
);

View File

@@ -0,0 +1,4 @@
<?php
return array(
// 'an english string' => 'The displayed english string',
);

View File

@@ -0,0 +1,4 @@
<?php
return array(
// 'an english string' => 'La traduction française de la chaine',
);

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 thelia-modules
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,63 @@
<?php
/*************************************************************************************/
/* This file is part of the module AdminOrderCreation */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace AdminOrderCreation\Model;
class Order extends \Thelia\Model\Order
{
public function getTotalAmountWithTax($withDiscount = true)
{
$total = 0;
foreach ($this->getOrderProducts() as $orderProduct) {
if ($orderProduct->getWasInPromo()) {
$total += $orderProduct->getPromoPrice() * $orderProduct->getQuantity();
} else {
$total += $orderProduct->getPrice() * $orderProduct->getQuantity();
}
foreach ($orderProduct->getOrderProductTaxes() as $orderProductTax) {
if ($orderProduct->getWasInPromo()) {
$total += $orderProductTax->getPromoAmount() * $orderProduct->getQuantity();
} else {
$total += $orderProductTax->getAmount() * $orderProduct->getQuantity();
}
}
}
$total += $this->getPostage();
if ($withDiscount) {
$total -= $this->getDiscount();
}
return $total;
}
public function getTotalAmountWithoutTax($withDiscount = true)
{
$total = 0;
foreach ($this->getOrderProducts() as $orderProduct) {
if ($orderProduct->getWasInPromo()) {
$total += $orderProduct->getPromoPrice() * $orderProduct->getQuantity();
} else {
$total += $orderProduct->getPrice() * $orderProduct->getQuantity();
}
}
$total += $this->getPostage() - $this->getPostageTax();
if ($withDiscount) {
$total -= $this->getDiscount();
}
return $total;
}
}

View File

@@ -0,0 +1,24 @@
# Admin Order Creation
Author: Gilles Bourgeat <gilles.bourgeat@gmail.com>
A module to create orders from the Thelia back-office.
## Compatibility
Thelia >= 2.3
## Installation
### Manually
* Copy the module into ```<thelia_root>/local/modules/``` directory and be sure that the name of the module is ```AdminOrderCreation```.
* Activate it in your thelia administration panel
### Composer
Add it in your main thelia composer.json file
```
composer require thelia/admin-order-creation ~1.3.3
```

View File

@@ -0,0 +1,39 @@
<?php
/*************************************************************************************/
/* This file is part of the module AdminOrderCreation */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace AdminOrderCreation\Util;
class Calc
{
public static function reduction($value, $type, $price, $quantity = 1)
{
$type = (int) $type;
$value = (float) $value;
$quantity = (float) $quantity;
if ($type === 1) {
if ($value < 0 || $value > 100) {
throw new \Exception('Invalid arg reduction');
}
return (($price / 100) * (100 - $value));
} elseif ($type === 2) {
if ($value < 0 || $value > $price * $quantity) {
throw new \Exception('Invalid arg reduction');
}
return ($price * $quantity - $value) / $quantity;
} elseif ($type === 3) {
if ($value < 0 || $value > $price) {
throw new \Exception('Invalid arg reduction');
}
return ($price - $value);
}
}
}

View File

@@ -0,0 +1,54 @@
<?php
/*************************************************************************************/
/* This file is part of the module AdminOrderCreation */
/* */
/* For the full copyright and license information, please view the LICENSE.txt */
/* file that was distributed with this source code. */
/*************************************************************************************/
namespace AdminOrderCreation\Util;
use Propel\Runtime\ActiveQuery\ModelCriteria;
trait CriteriaSearchTrait
{
/**
* @param string $q
* @return string
*/
public function getRegex($q)
{
$q = explode(' ', $q);
$words = array();
foreach ($q as $v) {
$v = trim($v);
if (strlen($v) > 2 && preg_match('/^[a-z0-9]+$/i', $v)) {
$words[] = $v;
}
}
if (!count($words)) {
return null;
}
$regex = array();
$regex[] = '.*' . implode('.+', $words) . '.*';
if (count($words) > 1) {
$regex[] = '.*' . implode('.+', array_reverse($words)) . '.*';
}
return implode('|', $regex);
}
/**
* @param ModelCriteria $query
* @param array $columns
* @param string $q
*/
public function whereConcatRegex(ModelCriteria $query, array $columns, $q)
{
$query->where("CONCAT_WS(' ', " . implode(',', $columns). ") REGEXP ?", self::getRegex($q), \PDO::PARAM_STR);
}
}

View File

@@ -0,0 +1,13 @@
{
"name": "thelia/admin-order-creation",
"description": "A module to create orders from the Thelia back-office.",
"license": "MIT",
"type": "thelia-module",
"require": {
"thelia/installer": "~1.1",
"thelia/credit-note-module": "~2.3.0"
},
"extra": {
"installer-name": "AdminOrderCreation"
}
}

View File

@@ -0,0 +1,5 @@
{check_auth role="ADMIN" resource="order" access="create"}
<div class="alert alert-success text-center">
<a href="{url path="/admin/order/update/%id" id=$order->getId()}">Voir la commande</a>
</div>

View File

@@ -0,0 +1,592 @@
{form name="admin-order-creation.create"}
<form method="POST" action="{url path="/admin/admin-order-creation/ajax/modal/create"}">
{form_field field='action'}
<input type="hidden" name="{$name}" value="create" />
{/form_field}
{if $form_error}<div class="alert alert-danger">{$form_error_message nofilter}</div>{/if}
<div class="row">
<div class="col-md-6">
<div class="alert alert-info">
{$currency_id = 0}
{form_field field='currency_id'}
{capture name="currency"}
<select name="{$name}" class="js-field-currency">
{loop type="currency" name="currency"}
<option value="{$ID}" {if !$data and $IS_DEFAULT}selected{/if}{if $data == $ID}selected{/if}>{$SYMBOL}</option>
{if $data == $ID}
{$currency_id = $ID}
{/if}
{if !$data and $IS_DEFAULT}
{$currency_id = $ID}
{/if}
{/loop}
</select>
{/capture}
{intl l="This order use the currency : %currency" d="adminordercreation.bo.default" currency=$smarty.capture.currency}
{/form_field}
</div>
</div>
<div class="col-md-6">
<div class="alert alert-info">
{$country_id = 0}
{form_field field='country_id'}
{capture name="country"}
<select name="{$name}" class="js-field-country">
{loop type="country" name="country"}
<option value="{$ID}" {if !$data and $IS_DEFAULT}selected{/if}{if $data == $ID}selected{/if}>{$TITLE}</option>
{if $data == $ID}
{$country_id = $ID}
{/if}
{if !$data and $IS_DEFAULT}
{$country_id = $ID}
{/if}
{/loop}
</select>
{/capture}
{intl l="This order use the country : %country" d="adminordercreation.bo.default" country=$smarty.capture.country}
{/form_field}
</div>
</div>
{* status select *}
<div class="col-md-6">
<div class="form-group">
<label>{intl l="Status :" d="adminordercreation.bo.default"}</label>
{$ignoreStatus = false}
{form_field field='credit_note_id'}
{if $data}
{$ignoreStatus = true}
{/if}
{/form_field}
{form_field field='status_id'}
<select class="form-control js-select-status" name="{$name}">
{loop type="order-status" name="order-status"}
{if !$ignoreStatus || ($ignoreStatus && $ID >= {$configPayedOrderMinimumStatusId})}
<option value="{$ID}" {if $order->getStatusId() == $ID}selected{/if} data-color="{$COLOR}">
{if $TITLE}{$TITLE}{else}{$CODE}{/if}
</option>
{/if}
{/loop}
</select>
{/form_field}
</div>
</div>
{* customer select *}
<div class="col-md-6">
<div class="form-group">
<label>{intl l="Customer :" d="adminordercreation.bo.default"}</label>
{form_field field='customer_id'}
<select class="form-control js-select-customer" name="{$name}" data-placeholder="{intl l="Search..." d="adminordercreation.bo.default"}" data-url="{url path="/admin/admin-order-creation/ajax/search/customer"}">
{if $order->getCustomer()}
{loop type="customer" name="customer" backend_context=true current=false id=$order->getCustomer()->getId()}
<option value="{$ID}">{$REF} : ({$FIRSTNAME} {$LASTNAME})</option>
{/loop}
{else}
<option value="">{intl l="Search..." d="adminordercreation.bo.default"}</option>
{/if}
</select>
{/form_field}
</div>
</div>
{* delivery module *}
<div class="col-md-6">
<div class="form-group">
<label>{intl l="Delivery module :" d="adminordercreation.bo.default"}</label>
{form_field field='delivery_module_id'}
<select class="form-control js-select-delivery-module" name="{$name}" data-placeholder="{intl l="Search..." d="adminordercreation.bo.default"}" >
{loop type="module" name="module-delivery" module_type=2 active="yes"}
<option value="{$ID}" {if $data == $ID}selected{/if}>{$TITLE}</option>
{/loop}
</select>
{/form_field}
</div>
</div>
{* payment module *}
<div class="col-md-6">
<div class="form-group">
<label>{intl l="Payment module :" d="adminordercreation.bo.default"}</label>
{form_field field='payment_module_id'}
<select class="form-control js-select-payment-module" name="{$name}" data-placeholder="{intl l="Search..." d="adminordercreation.bo.default"}" >
{loop type="module" name="module-payment" module_type=3 active="yes"}
<option value="{$ID}" {if $data == $ID}selected{/if}>{$TITLE}</option>
{/loop}
</select>
{/form_field}
</div>
</div>
{* address invoice *}
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">{intl l="Invoice address" d="adminordercreation.bo.default"}</div>
<div class="panel-body">
{$other = false}
<div class="form-group">
{form_field field='invoice_address_id'}
{if !$data}
{$other = true}
{/if}
<select class="form-control js-select-invoice-address" name="{$name}" {if !$order->getCustomer()}disabled{/if}>
{if $order->getCustomer()}
{loop type="address" name="address-invoice" customer=$order->getCustomer()->getId()}
<option value="{$ID}" {if $data == $ID}selected{/if}>
({$FIRSTNAME} {$LASTNAME}) : {$ADDRESS1} {$CITY} {$ZIPCODE}
</option>
{/loop}
<option value="" {if $other}selected{/if}>{intl l="Other" d="adminordercreation.bo.default"}</option>
{/if}
</select>
{/form_field}
</div>
<div class="row js-other-area {if !$other or !$order->getCustomer()}hide{/if}">
{form_field field='invoice_address_title'}
<div class="form-group col-md-3">
<label for="">{intl l="Title :" d="adminordercreation.bo.default"}</label>
<select name="{$name}" class="form-control" >
{loop type="title" name="title.invoice"}
<option value="{$ID}" {if $data == $ID}selected{/if} >{$LONG}</option>
{/loop}
</select>
</div>
{/form_field}
{form_field field='invoice_address_firstname'}
<div class="form-group col-md-4">
<label for="">{intl l="Firstname :" d="adminordercreation.bo.default"}</label>
<input type="text" name="{$name}" class="form-control" value="{$data}" />
</div>
{/form_field}
{form_field field='invoice_address_lastname'}
<div class="form-group col-md-5">
<label for="">{intl l="Lastname :" d="adminordercreation.bo.default"}</label>
<input type="text" name="{$name}" class="form-control" value="{$data}" />
</div>
{/form_field}
{form_field field='invoice_address_address1'}
<div class="form-group col-md-12">
<label for="">{intl l="Address :" d="adminordercreation.bo.default"}</label>
<input type="text" name="{$name}" class="form-control" value="{$data}" />
</div>
{/form_field}
{form_field field='invoice_address_address2'}
<div class="form-group col-md-12">
<label for="">{intl l="Address complement :" d="adminordercreation.bo.default"}</label>
<input type="text" name="{$name}" class="form-control" value="{$data}" />
</div>
{/form_field}
{form_field field='invoice_address_city'}
<div class="form-group col-md-6">
<label for="">{intl l="City :" d="adminordercreation.bo.default"}</label>
<input type="text" name="{$name}" class="form-control" value="{$data}" />
</div>
{/form_field}
{form_field field='invoice_address_zipcode'}
<div class="form-group col-md-6">
<label for="">{intl l="Zipcode :" d="adminordercreation.bo.default"}</label>
<input type="text" name="{$name}" class="form-control" value="{$data}" />
</div>
{/form_field}
{form_field field='invoice_address_country_id'}
<div class="form-group col-md-6">
<label for="">{intl l="Country :" d="adminordercreation.bo.default"}</label>
<select name="{$name}" class="form-control">
{if $data}
{$data = 64}
{loop type="country" name="country-address-invoice"}
{if $IS_DEFAULT}
{$data = $ID}
{/if}
{/loop}
{/if}
{loop type="country" name="country-address-invoice" visible=true}
<option value="{$ID}" {if $data == $ID}selected{/if}>{$TITLE}</option>
{/loop}
</select>
</div>
{/form_field}
{form_field field='invoice_address_company'}
<div class="form-group col-md-6">
<label for="">{intl l="Company :" d="adminordercreation.bo.default"}</label>
<input type="text" name="{$name}" class="form-control" value="{$data}" />
</div>
{/form_field}
</div>
</div>
</div>
</div>
{* delivery invoice *}
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">{intl l="Delivery address" d="adminordercreation.bo.default"}</div>
<div class="panel-body">
{$other = false}
<div class="form-group">
{form_field field='delivery_address_id'}
{if !$data}
{$other = true}
{/if}
<select class="form-control js-select-delivery-address" name="{$name}" {if !$order->getCustomer()}disabled{/if}>
{if $order->getCustomer()}
{loop type="address" name="address-delivery" customer=$order->getCustomer()->getId()}
<option value="{$ID}" {if $data == $ID}selected{/if}>
({$FIRSTNAME} {$LASTNAME}) : {$ADDRESS1} {$CITY} {$ZIPCODE}
</option>
{/loop}
<option value="" {if $other}selected{/if}>{intl l="Other" d="adminordercreation.bo.default"}</option>
{/if}
</select>
{/form_field}
</div>
<div class="row js-other-area {if !$other or !$order->getCustomer()}hide{/if}">
{form_field field='delivery_address_title'}
<div class="form-group col-md-3">
<label for="">{intl l="Title :" d="adminordercreation.bo.default"}</label>
<select name="{$name}" class="form-control" >
{loop type="title" name="title-delivery"}
<option value="{$ID}" {if $data == $ID}selected{/if} >{$LONG}</option>
{/loop}
</select>
</div>
{/form_field}
{form_field field='delivery_address_firstname'}
<div class="form-group col-md-4">
<label for="">{intl l="Firstname :" d="adminordercreation.bo.default"}</label>
<input type="text" name="{$name}" class="form-control" value="{$data}"/>
</div>
{/form_field}
{form_field field='delivery_address_lastname'}
<div class="form-group col-md-5">
<label for="">{intl l="Lastname :" d="adminordercreation.bo.default"}</label>
<input type="text" name="{$name}" class="form-control" value="{$data}"/>
</div>
{/form_field}
{form_field field='delivery_address_address1'}
<div class="form-group col-md-12">
<label for="">{intl l="Address :" d="adminordercreation.bo.default"}</label>
<input type="text" name="{$name}" class="form-control" value="{$data}"/>
</div>
{/form_field}
{form_field field='delivery_address_address2'}
<div class="form-group col-md-12">
<label for="">{intl l="Address complement :" d="adminordercreation.bo.default"}</label>
<input type="text" name="{$name}" class="form-control" value="{$data}"/>
</div>
{/form_field}
{form_field field='delivery_address_city'}
<div class="form-group col-md-6">
<label for="">{intl l="City :" d="adminordercreation.bo.default"}</label>
<input type="text" name="{$name}" class="form-control" value="{$data}"/>
</div>
{/form_field}
{form_field field='delivery_address_zipcode'}
<div class="form-group col-md-6">
<label for="">{intl l="Zipcode :" d="adminordercreation.bo.default"}</label>
<input type="text" name="{$name}" class="form-control" value="{$data}"/>
</div>
{/form_field}
{form_field field='delivery_address_country_id'}
<div class="form-group col-md-6">
<label for="">{intl l="Country :" d="adminordercreation.bo.default"}</label>
<select name="{$name}" class="form-control">
{if $data}
{$data = 64}
{loop type="country" name="country-address-delivery"}
{if $IS_DEFAULT}
{$data = $ID}
{/if}
{/loop}
{/if}
{loop type="country" name="country-address-delivery" visible=true}
<option value="{$ID}" {if $data == $ID}selected{/if}>{$TITLE}</option>
{/loop}
</select>
</div>
{/form_field}
{form_field field='delivery_address_company'}
<div class="form-group col-md-6">
<label for="">{intl l="Company :" d="adminordercreation.bo.default"}</label>
<input type="text" name="{$name}" class="form-control" value="{$data}"/>
</div>
{/form_field}
</div>
</div>
</div>
</div>
{* Product *}
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading">{intl l="Products" d="adminordercreation.bo.default"}</div>
<div class="panel-body">
{$count = 0}
{form_field field='product_id'}
{foreach from=$data item=item key=key}
{if $key > $count}
{$count = $key}
{/if}
{/foreach}
{/form_field}
<table class="table js-table-product-line">
<thead>
<tr>
<th width="30%">
{intl l="Product" d="adminordercreation.bo.default"} / {intl l="Product sale element" d="adminordercreation.bo.default"}
</th>
<th width="15%">
{intl l="Unit price with tax" d="adminordercreation.bo.default"}
</th>
<th width="10%">
{intl l="Quantity" d="adminordercreation.bo.default"}
</th>
<th width="20%">
{intl l="Price" d="adminordercreation.bo.default"}
</th>
<th width="5%">
<button class="btn btn-success js-action-add" data-key="{$count}" title="{intl l="Add product" d="adminordercreation.bo.default"}">+</button>
</th>
</tr>
</thead>
<tbody>
{form_field field='product_id'}
{foreach from=$order->getOrderProducts() item=orderProduct key=key}
{include file="admin-order-creation/include/product-line.html" orderProduct=$orderProduct}
{/foreach}
{/form_field}
</tbody>
</table>
</div>
</div>
</div>
{* Shipping *}
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading">{intl l="Shipping" d="adminordercreation.bo.default"}</div>
<div class="panel-body js-shipping-area">
<div class="row">
<div class="col-md-4">
{form_field field='shipping_price'}
<div class="form-group">
<label for="">{intl l="Price without tax :" d="adminordercreation.bo.default"}</label>
<input name="{$name}" class="form-control js-field-amount-without-tax" type="number" min="0" step="0.000001" value="{$data}" data-url="{url path="/admin/product/calculate-raw-price?action=to_tax"}" />
</div>
{/form_field}
</div>
<div class="col-md-4">
{form_field field='shipping_tax_rule_id'}
<div class="form-group">
<label for="">{intl l="Tax rule :" d="adminordercreation.bo.default"}</label>
<select name="{$name}" class="form-control js-field-tax-rule" name="{$name}" data-url="{url path="/admin/product/calculate-raw-price?action=from_tax"}">
{loop type="tax-rule" name="tax-rule"}
<option value="{$ID}" {if $data == $ID}selected{/if}>{$TITLE}</option>
{/loop}
</select>
</div>
{/form_field}
</div>
<div class="col-md-4">
{form_field field='shipping_price_with_tax'}
<div class="form-group">
<label for="">{intl l="Price with tax :" d="adminordercreation.bo.default"}</label>
<input name="{$name}" class="form-control js-field-amount-with-tax" type="number" min="0" step="0.000001" value="{$data}" data-url="{url path="/admin/product/calculate-raw-price?action=from_tax"}" />
</div>
{/form_field}
</div>
</div>
</div>
</div>
</div>
{* Global reduction *}
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading">{intl l="Global reduction" d="adminordercreation.bo.default"}</div>
<div class="panel-body">
<div class="row">
<div class="col-md-3">
<div class="form-group">
{form_field field='reduction_type'}
<div class="form-group">
<label for="">{intl l="Type :" d="adminordercreation.bo.default"}</label>
<select class="form-control js-action-refresh" name="{$name}">
<option value="1" {if $data == 1}selected{/if}>{intl l="Percentage" d="adminordercreation.bo.default"}</option>
{*<option value="2" {if $data == 2}selected{/if}>{intl l="Global amount" d="adminordercreation.bo.default"}</option>*}
</select>
</div>
{/form_field}
</div>
</div>
<div class="col-md-3">
{form_field field='reduction'}
<div class="form-group">
<label for="">{intl l="Value :" d="adminordercreation.bo.default"}</label>
<input class="form-control js-action-refresh" type="number" min="0" step="0.01" name="{$name}" value="{$data}" />
</div>
{/form_field}
</div>
</div>
</div>
</div>
</div>
{if $hasCreditNoteModule}
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading">{intl l="Use Credit Note :" d="adminordercreation.bo.default"}</div>
<div class="panel-body">
<div class="row">
{* customer select *}
<div class="col-md-6">
<div class="form-group">
<label>{intl l="Credit note :" d="adminordercreation.bo.default"}</label>
{$credit_note_id = null}
{form_field field='credit_note_id'}
<select class="form-control js-select-credit-note" name="{$name}" data-placeholder="{intl l="Search..." d="adminordercreation.bo.default"}" {if !$order->getCustomer()}disabled{/if}>
<option value=""></option>
{if $order->getCustomer()}
{loop type="credit-note" name="credit-note" backend_context=true customer_id=$order->getCustomer()->getId() invoiced=true used=false}
{if $data == $ID}
{$credit_note_id = $ID}
{/if}
<option value="{$ID}" {if $ID == $data}selected{/if}>{$REF} - {$INVOICE_REF} : {format_money number=$TOTAL_PRICE_WITH_TAX currency_id=$CURRENCY_ID}</option>
{/loop}
{/if}
</select>
{/form_field}
</div>
</div>
<div class="col-md-6">
{if $credit_note_id}
{loop type="credit-note" name="credit-note" backend_context=true id=$credit_note_id}
<div class="alert alert-info">
{intl l="This command uses the credit note ref %ref for the amount of %amount with tax." d="adminordercreation.bo.default"
ref=$REF
amount={format_money number=$TOTAL_PRICE_WITH_TAX currency_id=$CURRENCY_ID}
}
</div>
{$diffAmount = ($order->getTotalAmountWithTax() - $TOTAL_PRICE_WITH_TAX)|round:2}
{if $diffAmount == 0}
{elseif $diffAmount > 0}
<div class="alert alert-warning">
{intl l="Your customer will have to pay the difference of %amount with tax" d="adminordercreation.bo.default"
amount={format_money number={$order->getTotalAmountWithTax() - $TOTAL_PRICE_WITH_TAX} currency_id=$CURRENCY_ID}
}
</div>
{else}
<div class="alert alert-warning">
{intl l="A new credit note of %amount with tax will be issued for this customer." d="adminordercreation.bo.default"
amount={format_money number=-{$order->getTotalAmountWithTax() - $TOTAL_PRICE_WITH_TAX} currency_id=$CURRENCY_ID}
}
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label>{intl l="Status of new credit note :" d="adminordercreation.bo.default"}</label>
{form_field field='credit_note_status_id'}
{if !$data}
{$data = $configNewCreditNoteStatusId}
{/if}
<select class="form-control js-select-credit-note-status" name="{$name}" {if $invoiced}disabled{/if}>
{loop type="credit-note-status" name="credit-note-status" invoiced=true}
<option value="{$ID}" data-color="{$COLOR}" {if $data == $ID}selected{/if}>
{if $TITLE}{$TITLE}{else}{$CODE}{/if}
</option>
{/loop}
</select>
{/form_field}
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label>{intl l="Type of new credit note :" d="adminordercreation.bo.default"}</label>
{form_field field='credit_note_type_id'}
{if !$data}
{$data = $configNewCreditNoteTypeId}
{/if}
<select class="form-control js-select-credit-note-type" name="{$name}" {if $invoiced}disabled{/if}>
{loop type="credit-note-type" name="credit-note-type"}
<option value="{$ID}" data-color="{$COLOR}" {if $data == $ID}selected{/if}>
{if $TITLE}{$TITLE}{else}{$CODE}{/if}
</option>
{/loop}
</select>
{/form_field}
</div>
</div>
</div>
{/if}
{/loop}
{/if}
</div>
</div>
</div>
</div>
</div>
{/if}
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading">{intl l="Order Informations :" d="adminordercreation.bo.default"}</div>
<div class="panel-body">
<div class="row">
<div class="col-md-4 text-center">
<h2>
<span>{intl l="Total without tax :" d="adminordercreation.bo.default"}</span><span>
{format_money number=$order->getTotalAmountWithoutTax() currency_id=$order->getCurrency()->getId()} HT
</span>
</h2>
</div>
<div class="col-md-4 text-center">
<h2>
<span>{intl l="Total tax :" d="adminordercreation.bo.default"}</span><span>
{format_money number={$order->getTotalAmountWithTax()-$order->getTotalAmountWithoutTax()} currency_id=$order->getCurrency()->getId()}
</span>
</h2>
</div>
<div class="col-md-4 text-center">
<h2>
<span>{intl l="Total with tax :" d="adminordercreation.bo.default"}</span><span>
{format_money number=$order->getTotalAmountWithTax() currency_id=$order->getCurrency()->getId()} TTC
</span>
</h2>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-12 modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal" aria-hidden="true"><span class="glyphicon glyphicon-remove"></span>
{intl l="Cancel" d="adminordercreation.bo.default"}
</button>
<button type="submit" class="btn btn-primary">
{intl l="Create order" d="adminordercreation.bo.default"}
</button>
</div>
</div>
</form>
{/form}

View File

@@ -0,0 +1,465 @@
(function(){
var $modal = $('#modal-order-creation');
var currentRequest;
// fix bug bootstrap 3 and select2
$.fn.modal.Constructor.prototype.enforceFocus = function() {};
var timer = null;
function refreshWithTimer($form, event) {
if (timer !== null) {
clearTimeout(timer);
timer = null;
}
timer = setTimeout(function($form){
$modal.loadAjax(event, getFormData($form, {
'admin-order-creation-create[action]': 'refresh'
}));
}, 700, $form);
}
function initSelect($target, allowClear){
if (typeof allowClear === "undefined") {
allowClear = false;
}
return $target.select2({
allowClear: allowClear,
templateResult: function(data){
if (!data.id) return data.text;
var prefix = data.element.dataset.color ? '<span class="label" style="background-color: ' + data.element.dataset.color + ';width: 50px;">&nbsp;</span>' : '';
prefix += (data.element.dataset.ref ? ' <span style="font-weight: bold;">' + data.element.dataset.ref + '</span>' : '');
return $(prefix + ' <span>' + data.text + '</span>');
},
templateSelection: function(data){
if (!data.id) return data.text;
var prefix = data.element.dataset.color ? '<span class="label" style="background-color: ' + data.element.dataset.color + ';width: 50px;">&nbsp;</span>' : '';
prefix += (data.element.dataset.ref ? ' <span style="font-weight: bold;">' + data.element.dataset.ref + '</span>' : '');
return $(prefix + ' <span>' + data.text + '</span>');
}
});
}
function getFormData($form, data){
var formData = $form.serializeArray();
for (var i in formData) {
for (var e in data) {
if (formData[i].name === e) {
formData[i].value = data[e];
delete data[e];
}
}
}
for (var e in data) {
formData.push({
name: i,
value: data[i]
}
);
}
return formData
}
function initAjaxSelectCustomer($target){
return $target.select2({
ajax: {
url: $target.data('url'),
dataType: 'json',
delay: 250,
data: function (params){
return {
q: params.term,
customerId: $target.data('customer-id')
};
},
processResults: function (data){
return {results: data.items};
},
error: function(jqXHR, textStatus){
if (jqXHR.statusText === 'abort') return;
$target.select2('destroy');
$modal.displayError(jqXHR, textStatus);
},
cache: false
},
minimumInputLength: 3,
placeholder: $target.data('placeholder'),
templateResult: function(data){
if (data.loading) return data.text;
var markup = "<div class='select2-result-repository clearfix'>";
markup += data.ref + ' : (' + data.firstname + ' ' + data.lastname + ')' + '</br><small>' + data.address + '</small>';
markup += "</div>";
return $(markup);
},
templateSelection: function(data){
if (data.text) {
return data.text;
}
return data.ref + ' : (' + data.firstname + ' ' + data.lastname + ')';
}
});
}
function initAjaxSelectProduct($target){
return $target.select2({
ajax: {
url: $target.data('url'),
dataType: 'json',
delay: 250,
data: function (params){
return {
q: params.term
};
},
processResults: function (data){
return {results: data.items};
},
error: function(jqXHR, textStatus){
if (jqXHR.statusText === 'abort') return;
$target.select2('destroy');
$modal.displayError(jqXHR, textStatus);
},
cache: false
},
minimumInputLength: 3,
placeholder: $target.data('placeholder'),
templateResult: function(data){
if (data.loading) return data.text;
var markup = "<div class='select2-result-repository clearfix'>";
markup += data.ref + ' : ' + data.title;
markup += "</div>";
return $(markup);
},
templateSelection: function(data){
if (data.text) {
return data.text;
}
return data.ref + ' : ' + data.title;
}
});
}
/****** Modal methods ******/
$modal.loaderOff = function(){
$modal.find('.modal-loader').addClass('hidden');
$modal.find('.modal-body').removeClass('hidden');
};
$modal.loaderOn = function(){
$modal.find('.modal-loader').removeClass('hidden');
$modal.find('.modal-body').addClass('hidden');
};
$modal.reset = function(){
$modal.hideError();
$modal.loaderOn();
};
$modal.hideError = function(){
$modal.find('.modal-error').addClass('hidden').find('iframe').contents().find('html').empty();
};
$modal.displayError = function(jqXHR, textStatus){
if (jqXHR.statusText === 'abort') return;
$modal.loaderOff();
$modal.find('.modal-body').addClass('hidden');
var $error = $modal.find('.modal-error').removeClass('hidden');
$error.find('.textStatus').html(textStatus);
$error.find('iframe').contents().find('html').html(jqXHR.responseText);
};
$modal.modalReady = function(){
var $form = $modal.find('.modal-body form');
var $selectStatus = initSelect($form.find('.js-select-status'));
var $selectCustomer = initAjaxSelectCustomer($form.find('.js-select-customer'));
var $selectProduct = initAjaxSelectProduct($form.find('.js-select-product'));
var $selectProductSaleElement = initSelect($form.find('.js-select-product-sale-element'));
var $selectInvoiceAddress = initSelect($form.find('.js-select-invoice-address'));
var $selectDeliveryAddress = initSelect($form.find('.js-select-delivery-address'));
var $selectCreditNote = initSelect($form.find('.js-select-credit-note'), true);
var $selectCreditNoteStatus = initSelect($form.find('.js-select-credit-note-status'));
var $selectCreditNoteType = initSelect($form.find('.js-select-credit-note-type'));
$form.on('submit', function(event){
event.preventDefault();
$modal.loadAjax(event, getFormData($form, {
'admin-order-creation-create[action]': 'create'
}));
});
$selectStatus.on('select2:select', function(event){
$modal.loadAjax(event, getFormData($form, {
'admin-order-creation-create[action]': 'refresh'
}));
});
$selectCustomer.on('select2:select', function(event){
$modal.loadAjax(event, getFormData($form, {
'admin-order-creation-create[customer_id]': event.params.data.id,
'admin-order-creation-create[action]': 'refresh'
}));
});
$selectInvoiceAddress.on('select2:select', function(event){
$modal.loadAjax(event, getFormData($form, {
'admin-order-creation-create[action]': 'refresh'
}));
});
$selectDeliveryAddress.on('select2:select', function(event){
$modal.loadAjax(event, getFormData($form, {
'admin-order-creation-create[action]': 'refresh'
}));
});
$selectProduct.on('select2:select', function(event){
$(event.target).parents('tr').find('.js-refresh-price').val('1');
$modal.loadAjax(event, getFormData($form, {
'admin-order-creation-create[action]': 'refresh'
}));
});
$selectProductSaleElement.on('select2:select', function(event){
$(event.target).parents('tr').find('.js-refresh-price').val('1');
$modal.loadAjax(event, getFormData($form, {
'admin-order-creation-create[action]': 'refresh'
}));
});
$selectCreditNote.on('select2:select', function(event){
$modal.loadAjax(event, getFormData($form, {
'admin-order-creation-create[action]': 'refresh'
}));
});
$form.on('change', '.js-field-currency', function(event){
$modal.loadAjax(event, getFormData($form, {
'credit-note-create[action]': 'refresh'
}));
});
$form.on('change', '.js-action-refresh', function(event){
if ($(this).val().length) {
refreshWithTimer($form, event);
}
});
var currentProductRequestTax;
$form.on('change', '.js-product-price-with-tax, .js-product-price-without-tax', function(event){
if (currentProductRequestTax) currentProductRequestTax.abort();
var $th = $(this), $thr = $(this).parents('tr');
var val = parseFloat($(this).val());
if (!val) {
val = 0;
}
currentProductRequestTax = $.ajax({
url: $(this).data('url'),
dataType: 'json',
data: {
price: val,
tax_rule: this.dataset.taxRuleId
}
});
// ajax success
currentProductRequestTax.done(function(data){
if ($th.hasClass('js-product-price-without-tax')) {
$thr.find('.js-product-price-with-tax').val(data.result);
} else {
$thr.find('.js-product-price-without-tax').val(data.result);
}
refreshWithTimer($form, event);
});
// ajax error
currentProductRequestTax.fail(function(jqXHR, textStatus){
if (jqXHR.statusText === 'abort') return;
$modal.displayError(jqXHR, textStatus);
});
});
/***** Product line *****/
var $tableProductLine = $form.find('.js-table-product-line');
var templateProductLine = $('#template-order-creation-product-line').html();
$tableProductLine.on('click', '.js-action-add', function(event){
event.preventDefault();
$(this).data('key', parseInt($(this).data('key')) + 1);
var templateProductLineKey = templateProductLine.replace(/\[\]/g, '[' + $(this).data('key') + ']');
$tableProductLine.find('tbody').append(templateProductLineKey);
var $selectProduct = initAjaxSelectProduct($form.find('.js-table-product-line tbody .js-select-product').last());
$selectProduct.on('select2:select', function(event){
$modal.loadAjax(event, getFormData($form, {
'admin-order-creation-create[action]': 'refresh'
}));
});
/*if ($tableProductLine.find('tbody tr').not('.js-no-free-amount').length) {
$tableProductLine.find('.js-no-free-amount').addClass('hidden');
} else {
$tableProductLine.find('.js-no-free-amount').removeClass('hidden');
}*/
});
$tableProductLine.on('click', '.js-action-delete', function(event){
event.preventDefault();
$(this).parents('tr').remove();
$modal.loadAjax(event, getFormData($form, {
'admin-order-creation-create[action]': 'refresh'
}));
});
var $shippingArea = $form.find('.js-shipping-area');
var currentRequestTax;
$shippingArea.on('change', '.js-field-amount-without-tax, .js-field-amount-with-tax', function(event){
if (currentRequestTax) currentRequestTax.abort();
var $th = $(this), $thr = $shippingArea;
var val = parseFloat($(this).val());
if (!val) {
val = 0;
}
currentRequestTax = $.ajax({
url: $(this).data('url'),
dataType: 'json',
data: {
price: val,
tax_rule: parseInt($thr.find('.js-field-tax-rule').val())
}
});
// ajax success
currentRequestTax.done(function(data){
if ($th.hasClass('js-field-amount-without-tax')) {
$thr.find('.js-field-amount-with-tax').val(data.result);
} else {
$thr.find('.js-field-amount-without-tax').val(data.result);
}
refreshWithTimer($form, event);
});
// ajax error
currentRequestTax.fail(function(jqXHR, textStatus){
if (jqXHR.statusText === 'abort') return;
$modal.displayError(jqXHR, textStatus);
});
});
$shippingArea.on('change', '.js-field-tax-rule', function(event){
if (currentRequestTax) currentRequestTax.abort();
var $th = $(this), $thr = $shippingArea;
var val = parseFloat($thr.find('.js-field-amount-with-tax').val());
if (!val) {
val = 0;
}
currentRequestTax = $.ajax({
url: $(this).data('url'),
dataType: 'json',
data: {
price: val,
tax_rule: parseInt($thr.find('.js-field-tax-rule').val())
}
});
// ajax success
currentRequestTax.done(function(data){
if ($th.hasClass('js-field-amount-without-tax')) {
$thr.find('.js-field-amount-with-tax').val(data.result);
} else {
$thr.find('.js-field-amount-without-tax').val(data.result);
}
refreshWithTimer($form, event);
});
// ajax error
currentRequestTax.fail(function(jqXHR, textStatus){
if (jqXHR.statusText === 'abort') return;
$modal.displayError(jqXHR, textStatus);
});
});
};
$modal.loadAjax = function(event, data){
if (typeof data === 'undefined') {
data = {};
}
// kill last ajax request if not if it's not finished
if (currentRequest) currentRequest.abort();
// to avoid a display bug with select2
setTimeout(function(data){
// ajax start
currentRequest = $.ajax({
url: $modal.data('ajaxUrl'),
data: data,
method: 'POST'
});
// ajax success
currentRequest.done(function(data, textStatus, xhr){
$modal.loaderOff();
$modal.find('.modal-body').html(data);
$modal.modalReady();
});
// ajax error
currentRequest.fail(function(jqXHR, textStatus){
$modal.displayError(jqXHR, textStatus);
});
}, 100, data);
};
$('body').on('click', '#btn-create-order', function(event){
$modal.modal('show');
var customerId = $(this).data('customerId');
var creditNoteId = $(this).data('creditNoteId');
$modal.loadAjax(event, {
'admin-order-creation-create[action]': 'open',
'admin-order-creation-create[customer_id]': customerId,
'admin-order-creation-create[credit_note_id]': creditNoteId
});
});
$modal.on('hidden.bs.modal', function(){
$modal.reset();
});
}());

View File

@@ -0,0 +1,17 @@
<script type="text/javascript">
{loop type="order" name="from-order" id=$order_id customer="*" backend_context=true}
{$creditNoteId = null}
{loop type="credit-note" name="credit-note" order_id=$ID backend_context=true limit=1 used=false invoiced=true}
{$creditNoteId = $ID}
{/loop}
$('.general-block-decorator:first > .row:first > div:last').prepend('<button class="btn btn-sm btn-primary" {if $creditNoteId != null}data-credit-note-id="{$creditNoteId}"{/if} data-customer-id="{$CUSTOMER}" id="btn-create-order" style="margin-left: 10px;">\n' +
' {intl l="Create new order" d="adminordercreation.bo.default"}\n' +
'</button>');
{/loop}
</script>
<style>
.select2-selection__clear {
font-size: 20px;
color: red;
}
</style>

View File

@@ -0,0 +1,56 @@
<div class="modal fade" id="modal-order-creation" role="dialog" data-ajax-url="{url path="/admin/admin-order-creation/ajax/modal/create"}">
<div class="modal-dialog modal-lg" role="document" style="width: 90%; min-width: 1000px; max-width: 1500px;">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
</div>
<div class="modal-loader">
<br/><br/><div class="text-center"><span class="loading">{intl l="Please wait, loading" d="adminordercreation.bo.default"}</span></div><br/><br/>
</div>
<div class="modal-body">
</div>
<div class="modal-error hidden">
<div class="alert alert-danger">
<strong>{intl l="An error has occurred !!!" d="adminordercreation.bo.default"}</strong>
<br/>
<span>{intl l="Message status :" d="adminordercreation.bo.default"} <span class="textStatus"></span></span>
<br/>
<span>{intl l="Message content :" d="adminordercreation.bo.default"}</span>
<br/>
<iframe width="100%" style="height: 400px;"></iframe>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{intl l="Close" d="adminordercreation.bo.default"}</button>
</div>
</div>
</div>
</div>
</div>
<script type="text/html" id="template-order-creation-product-line">
{form name="admin-order-creation.create"}
{$key = ""}
{include file="admin-order-creation/include/product-line.html"}
{/form}
</script>
<link href="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.2/css/select2.min.css" rel="stylesheet" />
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.2/js/select2.min.js"></script>
{javascripts file='admin-order-creation/assets/js/script.js' source="AdminOrderCreation"}
<script src="{$asset_url}"></script>
{/javascripts}
<style>
{literal}
.js-list {
width: 100% !important;
}
.js-animate-info-button {
animation: AnimateInfoButton 2s infinite;
}
@keyframes AnimateInfoButton{
0%{opacity: 1;}
50%{opacity: 0;}
100%{opacity: 1;}
}
{/literal}
</style>

View File

@@ -0,0 +1,3 @@
<button class="btn btn-sm btn-primary pull-right" id="btn-create-order" style="margin-left: 10px;">
{intl l="Create new order" d="adminordercreation.bo.default"}
</button>

View File

@@ -0,0 +1,152 @@
<tr>
<td>
{form_field field='product_id' value_key=$key}
{if $orderProduct}
{loop type="product_sale_elements" order="id" name="product-sale-element-price-$key" id=$orderProduct->getProductSaleElementsId() currency=$currency_id backend_context=true}
{$product_id = $PRODUCT_ID}
{/loop}
{/if}
{$taxRuleId = null}
<div class="form-group">
<select class="form-control js-select-product" name="{$name}" data-placeholder="{intl l="Search..." d="adminordercreation.bo.default"}" data-url="{url path="/admin/admin-order-creation/ajax/search/product"}">
{if $product_id}
{loop type="product" name="product-$key" id=$product_id visible="*" backend_context=true}
{$taxRuleId = $TAX_RULE_ID}
<option value="{$ID}">{$REF} : {$TITLE}</option>
{/loop}
{/if}
</select>
</div>
{/form_field}
{form_field field='product_sale_element_id' value_key=$key}
<div class="form-group">
{if $product_id}
{$attributeCombinations = []}
{$PSE_first = null}
{loop type="product_sale_elements" order="id" name="product-sale-element-$key" product=$product_id currency=$currency_id backend_context=true}
{$PSE_id = $ID}
{if !$PSE_first}
{$PSE_first = $ID}
{/if}
{loop type="attribute_combination" name="attribute-combination-$key" product_sale_elements=$ID backend_context=true}
{$attributeCombinations[$PSE_id][] = $ATTRIBUTE_TITLE|cat:' : '|cat:$ATTRIBUTE_AVAILABILITY_TITLE}
{/loop}
{/loop}
{if $attributeCombinations|count}
<select class="form-control js-select-product-sale-element" name="{$name}" >
{loop type="product_sale_elements" order="id" name="product-sale-element-$key" product=$product_id currency=$currency_id}
{$selected = false}
{if !$orderProduct->getProductSaleElementsId() and $PSE_first == $ID}
{$productSaleEmenetIdSelected = $ID}
{$selected = true}
{elseif $orderProduct->getProductSaleElementsId() == $ID}
{$productSaleEmenetIdSelected = $ID}
{$selected = true}
{elseif $PSE_first == $ID}
{$productSaleEmenetIdSelected = $ID}
{$selected = true}
{/if}
<option {if $selected}selected{/if} data-quantity="{$QUANTITY}" data-ref="{$REF}" data-color="{if $QUANTITY > 0}#e6ffe6{else}#ffe6e6{/if}" value="{$ID}" >
{', '|implode:$attributeCombinations[$ID]}
</option>
{/loop}
</select>
{else}
{intl l="Default product sale element" d="adminordercreation.bo.default"}
{loop type="product_sale_elements" order="id" name="product-sale-element-$key" product=$product_id limit=1 currency=$currency_id backend_context=true}
{$productSaleEmenetIdSelected = $ID}
<input type="hidden" value="{$ID}" name="{$name}" />
{/loop}
{/if}
{/if}
</div>
{/form_field}
</td>
<td>
{if $product_id}
{if $productSaleEmenetIdSelected}
{loop type="product_sale_elements" order="id" name="product-sale-element-price-$key" id=$productSaleEmenetIdSelected currency=$currency_id backend_context=true}
{form_field field='refresh_price' value_key=$key}
<input class="js-refresh-price" type="hidden" name="{$name}" value="0" />
{/form_field}
{if $orderProduct->getWasInPromo()}
{$priceWithoutTax = $orderProduct->getPromoPrice()}
{else}
{$priceWithoutTax = $orderProduct->getPrice()}
{/if}
{$taxes = 0}
{foreach from=$orderProduct->getOrderProductTaxes() item=orderProductTax}
{if $orderProduct->getWasInPromo()}
{$taxes = $taxes + $orderProductTax->getPromoAmount()}
{else}
{$taxes = $taxes + $orderProductTax->getAmount()}
{/if}
{/foreach}
{form_field field='product_price_without_tax' value_key=$key}
<div class="form-group">
<div class="input-group">
<input class="form-control js-product-price-without-tax" data-tax-rule-id="{$taxRuleId}"
data-url="{url path="/admin/product/calculate-raw-price?action=to_tax"}"
type="number" min="0" step="0.000001" name="{$name}" value="{format_number number=$priceWithoutTax dec_point="." decimals="6"}" />
<div class="input-group-addon">HT</div>
</div>
</div>
{/form_field}
{form_field field='product_price_with_tax' value_key=$key}
<div class="form-group">
<div class="input-group">
<input class="form-control js-product-price-with-tax" data-tax-rule-id="{$taxRuleId}"
data-url="{url path="/admin/product/calculate-raw-price?action=from_tax"}"
type="number" min="0" step="0.000001" name="{$name}" value="{format_number number=($priceWithoutTax + $taxes) dec_point="." decimals="6"}" />
<div class="input-group-addon">TTC</div>
</div>
</div>
{/form_field}
{/loop}
{/if}
{/if}
</td>
<td>
{form_field field='product_quantity' value_key=$key}
<div class="form-group">
{if $product_id}
{loop type="product_sale_elements" order="id" name="product-sale-element-quanaity-$key" id=$productSaleEmenetIdSelected currency=$currency_id backend_context=true}
<input class="form-control js-action-refresh" type="number" min="1" step="1" name="{$name}" value="{$orderProduct->getQuantity()}" />
{/loop}
{/if}
</div>
{/form_field}
</td>
<td>
{if $productSaleEmenetIdSelected}
{loop type="product_sale_elements" order="id" name="product-sale-element-price-$key" id=$productSaleEmenetIdSelected currency=$currency_id backend_context=true}
<div class="form-group">
<div class="input-group">
<input class="form-control js-action-refresh" type="number" min="0" step="0.000001" disabled value="{format_number number=($priceWithoutTax * $orderProduct->getQuantity()) dec_point="." decimals="6"}" />
<div class="input-group-addon">HT</div>
</div>
</div>
<div class="form-group">
<div class="input-group">
<input class="form-control js-action-refresh" type="number" min="0" step="0.000001" disabled value="{format_number number=(($priceWithoutTax + $taxes) * $orderProduct->getQuantity()) dec_point="." decimals="6"}" />
<div class="input-group-addon">TTC</div>
</div>
</div>
{/loop}
{/if}
</td>
<td>
<button class="btn btn-danger js-action-delete" title="{intl l="Delete" d="adminordercreation.bo.default"}">
<span class="glyphicon glyphicon-trash"></span>
</button>
</td>
</tr>

View File

@@ -0,0 +1,18 @@
<?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">
<commands>
<command class="PurgeFakeCustomer\Command\FakeCustomerPurge" />
</commands>
<services>
<service id="purgefakecustomer.purge" class="PurgeFakeCustomer\EventListener\EventManager">
<argument type="service" id="event_dispatcher" />
<tag name="kernel.event_subscriber"/>
</service>
</services>
</config>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="http://thelia.net/schema/dic/module"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://thelia.net/schema/dic/module http://thelia.net/schema/dic/module/module-2_2.xsd">
<fullnamespace>PurgeFakeCustomer\PurgeFakeCustomer</fullnamespace>
<descriptive locale="en_US">
<title>Delete fake customers</title>
</descriptive>
<descriptive locale="fr_FR">
<title>Supprime des faux comptes clients</title>
</descriptive>
<languages>
<language>en_US</language>
<language>fr_FR</language>
</languages>
<version>1.0</version>
<authors>
<author>
<name>Laurent LE CORRE</name>
<email>laurent@thecoredev.fr</email>
</author>
</authors>
<type>classic</type>
<thelia>2.3.0</thelia>
<stability>beta</stability>
<mandatory>0</mandatory>
<hidden>0</hidden>
</module>

View File

@@ -0,0 +1,12 @@
<?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="purgefakecustomer.purge" path="/purgefakecustomer/{secretKey}">
<default key="_controller">PurgeFakeCustomer\Controller\PurgeController::purge</default>
<requirement key="secretKey">.+</requirement>
</route>
</routes>

View File

@@ -0,0 +1,11 @@
<?php
namespace PurgeFakeCustomer;
use Thelia\Module\BaseModule;
class PurgeFakeCustomer extends BaseModule
{
/** @var string */
const DOMAIN_NAME = 'purgefakecustomer';
}

View File

@@ -0,0 +1,55 @@
# Purge Fake Customer
Add a short description here. You can also add a screenshot if needed.
## Installation
### Manually
* Copy the module into ```<thelia_root>/local/modules/``` directory and be sure that the name of the module is PurgeFakeCustomer.
* Activate it in your thelia administration panel
### Composer
Add it in your main thelia composer.json file
```
composer require your-vendor/purge-fake-customer-module:~1.0
```
## Usage
Explain here how to use your module, how to configure it, etc.
## Hook
If your module use one or more hook, fill this part. Explain which hooks are used.
## Loop
If your module declare one or more loop, describe them here like this :
[loop name]
### Input arguments
|Argument |Description |
|--- |--- |
|**arg1** | describe arg1 with an exemple. |
|**arg2** | describe arg2 with an exemple. |
### Output arguments
|Variable |Description |
|--- |--- |
|$VAR1 | describe $VAR1 variable |
|$VAR2 | describe $VAR2 variable |
### Exemple
Add a complete exemple of your loop
## Other ?
If you have other think to put, feel free to complete your readme as you want.

View File

@@ -0,0 +1,11 @@
{
"name": "your-vendor/purge-fake-customer-module",
"license": "LGPL-3.0+",
"type": "thelia-module",
"require": {
"thelia/installer": "~1.1"
},
"extra": {
"installer-name": "PurgeFakeCustomer"
}
}