Commit 10c6b239 by Damien Moulard

Merge branch '5691-paiement-reccurent-payzen' into 'ssa-gironde'

payzen preccurent payment

See merge request cooperatic/kohinos-tav!77
parents 41fca9e5 ff90af24
...@@ -50,6 +50,7 @@ PRESTA_SELF_INIT_AND_EVAL=0 ...@@ -50,6 +50,7 @@ PRESTA_SELF_INIT_AND_EVAL=0
AUTOMATISATION_RECONVERSION=0 AUTOMATISATION_RECONVERSION=0
PRESTA_EXTRA_DATA=0 PRESTA_EXTRA_DATA=0
HOUSEHOLD_BASED_ALLOWANCE=0 HOUSEHOLD_BASED_ALLOWANCE=0
SSA_FRIENDLY_FLUX_TYPE_NAMES=0
EMAIL_ERROR=technique@kohinos.net EMAIL_ERROR=technique@kohinos.net
EMAIL_USER_FROM=noreply@kohinos.fr EMAIL_USER_FROM=noreply@kohinos.fr
......
...@@ -68,4 +68,5 @@ templates/themes/custom/ ...@@ -68,4 +68,5 @@ templates/themes/custom/
.idea/ .idea/
#dir containg exported reconversions xml #dir containg exported reconversions xml
/reconversions/ reconversions/*
!reconversions/.versionme
...@@ -58,6 +58,7 @@ Copier le fichier .env.dist en .env et configurer : ...@@ -58,6 +58,7 @@ Copier le fichier .env.dist en .env et configurer :
- en environnement TAV, la variable AUTOMATISATION_RECONVERSION permet d'activer l'automatisation des reconversions - en environnement TAV, la variable AUTOMATISATION_RECONVERSION permet d'activer l'automatisation des reconversions
- en environnement TAV, la variable PRESTA_EXTRA_DATA permet d'indiquer puis d'afficher publiquement plus de données concernant les prestataires (e.g. familles de produits) - en environnement TAV, la variable PRESTA_EXTRA_DATA permet d'indiquer puis d'afficher publiquement plus de données concernant les prestataires (e.g. familles de produits)
- en environnement TAV, la variable HOUSEHOLD_BASED_ALLOWANCE permet d'activer un mode de cotisations libres et d'allocations basées sur la composition du foyer - en environnement TAV, la variable HOUSEHOLD_BASED_ALLOWANCE permet d'activer un mode de cotisations libres et d'allocations basées sur la composition du foyer
- en environnement TAV, la variable SSA_FRIENDLY_FLUX_TYPE_NAMES permet d'adapter le nommage des types de flux à un projet de type ssa
Si vous utilisez Payzen comme moyen de paiement par CB : Si vous utilisez Payzen comme moyen de paiement par CB :
...@@ -148,7 +149,9 @@ Une fois tout installé, éventuellement changer le logo et favicon situés dans ...@@ -148,7 +149,9 @@ Une fois tout installé, éventuellement changer le logo et favicon situés dans
Une fois Payzen configuré sur le Kohinos, il faut le configurer sur le Back Office de Payzen : Une fois Payzen configuré sur le Kohinos, il faut le configurer sur le Back Office de Payzen :
Paramétrage -> Boutique : configurer les 2 "URL de retour de la boutique" avec la page d'accueil du site. Paramétrage -> Boutique : configurer les 2 "URL de retour de la boutique" avec la page d'accueil du site.
Paramétrage -> Règles de notification -> URL de notification à la fin du paiement : dans le 2ème encadré, renseigner les 2 champs avec [url_du_site]/payment/done Paramétrage -> Règles de notification -> URL de notification à la fin du paiement : dans le 2ème encadré, renseigner les 2 champs avec [url_du_site]/payment/notify
Paramétrage -> Règles de notification -> URL de notification à la création d'un abonnement (mal nommé à mon avis car utilisé à chaque échéance) : dans le 2ème encadré, renseigner les 2 champs avec [url_du_site]/payment/notify
Remarque : les URLS de notification sont réécrites par Payum à la même valeur dans le cadre de sa configuration par défaut
Une fois l'installation et la configuration du Kohinos terminées, il est conseillé de réaliser un premier paiement afin de tester le bon fonctionnement de la communication avec Payzen en environnement de production. Une fois l'installation et la configuration du Kohinos terminées, il est conseillé de réaliser un premier paiement afin de tester le bon fonctionnement de la communication avec Payzen en environnement de production.
......
...@@ -429,3 +429,8 @@ form[name="formEncaissement"] label { ...@@ -429,3 +429,8 @@ form[name="formEncaissement"] label {
.text-error { .text-error {
color: #ff4136; color: #ff4136;
} }
.tav-cotisation-payment-form {
display: none;
margin-top: 1rem;
}
\ No newline at end of file
...@@ -806,4 +806,26 @@ $(function() { ...@@ -806,4 +806,26 @@ $(function() {
$('.prestataire-product-families-select').on('change', onPrestataireProductFamilyChange); $('.prestataire-product-families-select').on('change', onPrestataireProductFamilyChange);
}; };
$("#add-prestataire-products-family").on("click", addFormToCollection); $("#add-prestataire-products-family").on("click", addFormToCollection);
$("#tav-cotisation-payment-type").on("change", function() {
$(".tav-cotisation-payment-form").hide();
if (this.value === "instant-payment") {
$("#tav-cotisation-instant-payment-container").show();
} else if (this.value === "recurrent-payment") {
$("#tav-cotisation-recurrent-payment-container").show();
}
});
$("input:text[name='formAchatMonnaieAdherentRecurrent[don][montant]']").on('input', function() {
let montant = 0;
montant = parseFloat($('input#formAchatMonnaieAdherentRecurrent_montant').val());
let montantDon = parseFloat($("input:text[name='formAchatMonnaieAdherentRecurrent[don][montant]']").val().replace(",", "."));
if (isNaN(montantDon)) {
montantDon = 0;
}
var valuetotal = montant + montantDon;
$("span.achat_monnaie_premier_montant_total").text(valuetotal + ' €')
});
}); });
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "b4f4376c4660e8a7ab3c2a419263798f", "content-hash": "9f1667eb8d3defce6dc7895876f07cd5",
"packages": [ "packages": [
{ {
"name": "alcohol/iso4217", "name": "alcohol/iso4217",
...@@ -2525,6 +2525,46 @@ ...@@ -2525,6 +2525,46 @@
"time": "2021-10-11T09:18:27+00:00" "time": "2021-10-11T09:18:27+00:00"
}, },
{ {
"name": "ekyna/payum-payzen",
"version": "dev-5691-paiement-reccurent-payzen",
"dist": {
"type": "path",
"url": "./lib/ekyna/payum-payzen",
"reference": "781b74869b2acc65e7635c5bd5d40c66cf1c2177"
},
"require": {
"payum/core": "^1.5",
"php": "^7.0",
"php-http/guzzle6-adapter": "^2.0",
"psr/log": "~1.0",
"symfony/process": "4.4.*"
},
"type": "component",
"autoload": {
"psr-4": {
"Ekyna\\Component\\Payum\\Payzen\\": ""
}
},
"license": [
"MIT"
],
"authors": [
{
"name": "Etienne Dauvergne",
"homepage": "http://ekyna.com"
}
],
"description": "PayZen Payum gateway",
"homepage": "https://github.com/ekyna/PayumPayzen",
"keywords": [
"payum",
"payzen"
],
"transport-options": {
"relative": true
}
},
{
"name": "exsyst/swagger", "name": "exsyst/swagger",
"version": "v0.4.2", "version": "v0.4.2",
"source": { "source": {
...@@ -19601,7 +19641,9 @@ ...@@ -19601,7 +19641,9 @@
], ],
"aliases": [], "aliases": [],
"minimum-stability": "stable", "minimum-stability": "stable",
"stability-flags": [], "stability-flags": {
"ekyna/payum-payzen": 20
},
"prefer-stable": false, "prefer-stable": false,
"prefer-lowest": false, "prefer-lowest": false,
"platform": { "platform": {
......
...@@ -16,3 +16,4 @@ twig: ...@@ -16,3 +16,4 @@ twig:
presta_extra_data: '%env(PRESTA_EXTRA_DATA)%' presta_extra_data: '%env(PRESTA_EXTRA_DATA)%'
household_based_allowance: '%env(HOUSEHOLD_BASED_ALLOWANCE)%' household_based_allowance: '%env(HOUSEHOLD_BASED_ALLOWANCE)%'
automatisation_reconversion: '%env(AUTOMATISATION_RECONVERSION)%' automatisation_reconversion: '%env(AUTOMATISATION_RECONVERSION)%'
ssa_friendly_flux_type_names: '%env(SSA_FRIENDLY_FLUX_TYPE_NAMES)%'
\ No newline at end of file
...@@ -17,6 +17,7 @@ parameters: ...@@ -17,6 +17,7 @@ parameters:
automatisation_reconversion: '%env(AUTOMATISATION_RECONVERSION)%' automatisation_reconversion: '%env(AUTOMATISATION_RECONVERSION)%'
presta_extra_data: '%env(PRESTA_EXTRA_DATA)%' presta_extra_data: '%env(PRESTA_EXTRA_DATA)%'
household_based_allowance: '%env(HOUSEHOLD_BASED_ALLOWANCE)%' household_based_allowance: '%env(HOUSEHOLD_BASED_ALLOWANCE)%'
ssa_friendly_flux_type_names: '%env(SSA_FRIENDLY_FLUX_TYPE_NAMES)%'
# PARAMETRES DES IMPORTS POSSIBLE POUR L'APPLICATION DE GESTION DE MONNAIE LOCALE COMPLEMENTAIRE # PARAMETRES DES IMPORTS POSSIBLE POUR L'APPLICATION DE GESTION DE MONNAIE LOCALE COMPLEMENTAIRE
...@@ -129,6 +130,10 @@ services: ...@@ -129,6 +130,10 @@ services:
public: true public: true
arguments: ['@app.utils.custom_entity_manager', '@security.helper', '@app.utils.operations'] arguments: ['@app.utils.custom_entity_manager', '@security.helper', '@app.utils.operations']
app.utils.payment:
class: App\Utils\PaymentUtils
autowire: true
app.twig.main.extension: app.twig.main.extension:
class: App\Twig\AppExtension class: App\Twig\AppExtension
autowire: false autowire: false
......
...@@ -61,6 +61,20 @@ class CaptureAction implements ActionInterface, GatewayAwareInterface, GenericTo ...@@ -61,6 +61,20 @@ class CaptureAction implements ActionInterface, GatewayAwareInterface, GenericTo
); );
$model['vads_url_check'] = $notifyToken->getTargetUrl(); $model['vads_url_check'] = $notifyToken->getTargetUrl();
} }
// Set recurrent payment data if needed
$payment = $request->getFirstModel();
if (true == $payment->getIsRecurrent()) {
$model['vads_page_action'] = 'REGISTER_PAY_SUBSCRIBE';
$model['vads_sub_amount'] = strval($payment->getRecurrenceAmount()); // 1000 for 10.00 EUR
$model['vads_sub_currency'] = $model['vads_currency'];
$model['vads_sub_effect_date'] = (new \DateTime('tomorrow', new \DateTimeZone('UTC')))->format('Ymd'); // tomorrow, to avoid duplicate payment this day
//FOR TEST : $model['vads_sub_desc'] = 'RRULE:FREQ=DAILY;INTERVAL=1;COUNT=2';
$count = $payment->getRecurrenceMonthsCount() - 1; //initial payment is not considered by payzen as the first occurence
$monthDay = $payment->getRecurrenceMonthDay();
$model['vads_sub_desc'] = "RRULE:FREQ=MONTHLY;COUNT={$count};BYMONTHDAY={$monthDay}";
}
} }
if (false == $model['vads_trans_id']) { if (false == $model['vads_trans_id']) {
......
...@@ -126,7 +126,6 @@ class Api ...@@ -126,7 +126,6 @@ class Api
$data = $this $data = $this
->getRequestOptionsResolver() ->getRequestOptionsResolver()
->resolve(array_replace($data, [ ->resolve(array_replace($data, [
'vads_page_action' => 'PAYMENT',
'vads_version' => 'V2', 'vads_version' => 'V2',
])); ]));
...@@ -353,6 +352,10 @@ class Api ...@@ -353,6 +352,10 @@ class Api
'vads_url_return' => null, // Obligatoire si acquisition de la carte par commerçant 'vads_url_return' => null, // Obligatoire si acquisition de la carte par commerçant
'vads_user_info' => null, 'vads_user_info' => null,
'vads_version' => 'V2', 'vads_version' => 'V2',
'vads_sub_amount' => null,
'vads_sub_currency' => null,
'vads_sub_effect_date' => null,
'vads_sub_desc' => null,
]) ])
->setRequired([ ->setRequired([
'vads_amount', 'vads_amount',
...@@ -365,7 +368,7 @@ class Api ...@@ -365,7 +368,7 @@ class Api
->setAllowedValues('vads_action_mode', ['SILENT', 'INTERACTIVE']) ->setAllowedValues('vads_action_mode', ['SILENT', 'INTERACTIVE'])
->setAllowedValues('vads_currency', $this->getCurrencyCodes()) ->setAllowedValues('vads_currency', $this->getCurrencyCodes())
->setAllowedValues('vads_language', $this->getLanguageCodes()) ->setAllowedValues('vads_language', $this->getLanguageCodes())
->setAllowedValues('vads_page_action', 'PAYMENT') ->setAllowedValues('vads_page_action', ['PAYMENT', 'REGISTER_PAY_SUBSCRIBE'])
->setAllowedValues('vads_payment_cards', $this->getCardsCodes()) ->setAllowedValues('vads_payment_cards', $this->getCardsCodes())
->setAllowedValues('vads_payment_config', function ($value) { ->setAllowedValues('vads_payment_config', function ($value) {
if ($value === 'SINGLE') { if ($value === 'SINGLE') {
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
<server name="SYMFONY_PHPUNIT_REMOVE" value=""/> <server name="SYMFONY_PHPUNIT_REMOVE" value=""/>
<server name="SYMFONY_PHPUNIT_VERSION" value="7.5"/> <server name="SYMFONY_PHPUNIT_VERSION" value="7.5"/>
<server name="SYMFONY_DEPRECATIONS_HELPER" value="disabled" /> <server name="SYMFONY_DEPRECATIONS_HELPER" value="disabled" />
<env name="KERNEL_CLASS" value="App\Kernel"/>
</php> </php>
<testsuites> <testsuites>
<testsuite name="Project Test Suite"> <testsuite name="Project Test Suite">
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -3,10 +3,10 @@ ...@@ -3,10 +3,10 @@
"app": { "app": {
"js": [ "js": [
"/build/runtime.6ad5c9da.js", "/build/runtime.6ad5c9da.js",
"/build/app.27091a64.js" "/build/app.3644f7b2.js"
], ],
"css": [ "css": [
"/build/app.5cdc32e6.css" "/build/app.8a3f698b.css"
] ]
}, },
"admin": { "admin": {
......
{ {
"build/app.css": "/build/app.5cdc32e6.css", "build/app.css": "/build/app.8a3f698b.css",
"build/app.js": "/build/app.27091a64.js", "build/app.js": "/build/app.3644f7b2.js",
"build/admin.css": "/build/admin.4de55830.css", "build/admin.css": "/build/admin.4de55830.css",
"build/admin.js": "/build/admin.86a2d986.js", "build/admin.js": "/build/admin.86a2d986.js",
"build/runtime.js": "/build/runtime.6ad5c9da.js", "build/runtime.js": "/build/runtime.6ad5c9da.js",
......
...@@ -27,9 +27,17 @@ class AchatMonnaieAdmin extends FluxAdmin ...@@ -27,9 +27,17 @@ class AchatMonnaieAdmin extends FluxAdmin
public function createQuery($context = 'list') public function createQuery($context = 'list')
{ {
$query = parent::createQuery($context); $query = parent::createQuery($context);
//In mode ssa_friendly_flux_type_names, we display on this page all cotisation
//initial operation (i.e. we display vente_emlc operations in addition to achat_monnaie)
if($this->getConfigurationPool()->getContainer()->getParameter('tav_env')
&& $this->getConfigurationPool()->getContainer()->getParameter('ssa_friendly_flux_type_names')) {
$query->andWhere($query->getRootAliases()[0] . '.parenttype = :parenttype' . ' OR ' . $query->getRootAliases()[0] . '.parenttype = :parenttype2')
->setParameter('parenttype', Flux::TYPE_ACHAT)
->setParameter('parenttype2', Flux::TYPE_VENTE_EMLC);
} else {
$query->andWhere($query->getRootAliases()[0] . '.parenttype = :parenttype') $query->andWhere($query->getRootAliases()[0] . '.parenttype = :parenttype')
->setParameter('parenttype', Flux::TYPE_ACHAT); ->setParameter('parenttype', Flux::TYPE_ACHAT);
}
return $query; return $query;
} }
...@@ -43,6 +51,12 @@ class AchatMonnaieAdmin extends FluxAdmin ...@@ -43,6 +51,12 @@ class AchatMonnaieAdmin extends FluxAdmin
*/ */
protected function configureDatagridFilters(DatagridMapper $datagridMapper): void protected function configureDatagridFilters(DatagridMapper $datagridMapper): void
{ {
//In mode ssa_friendly_flux_type_names, we display on this page all cotisation
//initial operation (i.e. we display vente_emlc operations in addition to achat_monnaie)
//therefore filtering by type achat_monnaie_adherent VS achat_monnaie_prestataire
//would be confusing (selecting one of them would hide all vente_emlc operations)
if(!$this->getConfigurationPool()->getContainer()->getParameter('tav_env')
|| !$this->getConfigurationPool()->getContainer()->getParameter('ssa_friendly_flux_type_names')) {
$datagridMapper->add('type', null, [ $datagridMapper->add('type', null, [
'advanced_filter' => false, 'advanced_filter' => false,
'show_filter' => true, 'show_filter' => true,
...@@ -55,6 +69,7 @@ class AchatMonnaieAdmin extends FluxAdmin ...@@ -55,6 +69,7 @@ class AchatMonnaieAdmin extends FluxAdmin
], ],
]); ]);
} }
}
/** /**
* {@inheritdoc} * {@inheritdoc}
...@@ -81,12 +96,24 @@ class AchatMonnaieAdmin extends FluxAdmin ...@@ -81,12 +96,24 @@ class AchatMonnaieAdmin extends FluxAdmin
$datagrid->buildPager(); $datagrid->buildPager();
$query = clone $datagrid->getQuery(); $query = clone $datagrid->getQuery();
if($this->getConfigurationPool()->getContainer()->getParameter('tav_env')
&& $this->getConfigurationPool()->getContainer()->getParameter('ssa_friendly_flux_type_names')) {
$query
->select('SUM( ' . $query->getRootAlias() . '.montant) as total')
->andWhere($query->getRootAlias() . '.parenttype = :parenttype' . ' OR ' . $query->getRootAliases()[0] . '.parenttype = :parenttype2')
->setParameter('parenttype', Flux::TYPE_ACHAT)
->setParameter('parenttype2', Flux::TYPE_VENTE_EMLC)
->setFirstResult(null)
->setMaxResults(null);
} else {
$query $query
->select('SUM( ' . $query->getRootAlias() . '.montant) as total') ->select('SUM( ' . $query->getRootAlias() . '.montant) as total')
->andWhere($query->getRootAlias() . '.parenttype = :parenttype') ->andWhere($query->getRootAlias() . '.parenttype = :parenttype')
->setParameter('parenttype', Flux::TYPE_ACHAT) ->setParameter('parenttype', Flux::TYPE_ACHAT)
->setFirstResult(null) ->setFirstResult(null)
->setMaxResults(null); ->setMaxResults(null);
}
$result = $query->execute([], \Doctrine\ORM\Query::HYDRATE_SINGLE_SCALAR); $result = $query->execute([], \Doctrine\ORM\Query::HYDRATE_SINGLE_SCALAR);
......
...@@ -64,6 +64,8 @@ class FluxAdmin extends AbstractAdmin ...@@ -64,6 +64,8 @@ class FluxAdmin extends AbstractAdmin
protected function configureExportFields(): array protected function configureExportFields(): array
{ {
$ssaFriendlyTypeNames = $this->getConfigurationPool()->getContainer()->getParameter('tav_env')
&& $this->getConfigurationPool()->getContainer()->getParameter('ssa_friendly_flux_type_names');
return [ return [
$this->trans('Date') => 'createdAt', $this->trans('Date') => 'createdAt',
$this->trans('Expediteur') => 'expediteur', $this->trans('Expediteur') => 'expediteur',
...@@ -91,10 +93,12 @@ class FluxAdmin extends AbstractAdmin ...@@ -91,10 +93,12 @@ class FluxAdmin extends AbstractAdmin
*/ */
protected function configureListFields(ListMapper $listMapper) protected function configureListFields(ListMapper $listMapper)
{ {
$ssaFriendlyTypeNames = $this->getConfigurationPool()->getContainer()->getParameter('tav_env')
&& $this->getConfigurationPool()->getContainer()->getParameter('ssa_friendly_flux_type_names');
unset($this->listModes['mosaic']); unset($this->listModes['mosaic']);
$listMapper $listMapper
->add('createdAt', 'datetime', ['label' => 'Date']) ->add('createdAt', 'datetime', ['label' => 'Date'])
->add('type', 'text', ['label' => 'Type', 'template' => '@kohinos/bundles/SonataAdminBundle/Block/translated_value.html.twig']) ->add($ssaFriendlyTypeNames ? 'ssaFriendlyTypeName' : 'type', 'text', ['label' => 'Type', 'template' => '@kohinos/bundles/SonataAdminBundle/Block/translated_value.html.twig'])
->add('montant', 'decimal', ['label' => 'Montant', 'attributes' => ['fraction_digits' => 2]]) ->add('montant', 'decimal', ['label' => 'Montant', 'attributes' => ['fraction_digits' => 2]])
->add('expediteur', null, ['label' => 'Expediteur']) ->add('expediteur', null, ['label' => 'Expediteur'])
->add('destinataire', null, ['label' => 'Destinataire']) ->add('destinataire', null, ['label' => 'Destinataire'])
...@@ -110,6 +114,11 @@ class FluxAdmin extends AbstractAdmin ...@@ -110,6 +114,11 @@ class FluxAdmin extends AbstractAdmin
*/ */
protected function configureDatagridFilters(DatagridMapper $datagridMapper): void protected function configureDatagridFilters(DatagridMapper $datagridMapper): void
{ {
$ssaFriendlyTypeNames = $this->getConfigurationPool()->getContainer()->getParameter('tav_env')
&& $this->getConfigurationPool()->getContainer()->getParameter('ssa_friendly_flux_type_names');
$choices = $ssaFriendlyTypeNames ? //in ssa mode, remove flux types that are irrelevant or confusing
['Transactions' => Flux::TYPE_TRANSACTION]
: ['Cotisations' => Flux::TYPE_COTISATION, 'Achat de monnaie' => Flux::TYPE_ACHAT, 'Dépôt de billets' => Flux::TYPE_CHANGE, 'Retrait' => Flux::TYPE_RETRAIT, 'Transactions' => Flux::TYPE_TRANSACTION, 'Transferts' => Flux::TYPE_TRANSFERT, 'Vente' => Flux::TYPE_VENTE];
$datagridMapper $datagridMapper
->add('transfert_or_transaction', 'doctrine_orm_callback', [ ->add('transfert_or_transaction', 'doctrine_orm_callback', [
'label' => 'Type', 'label' => 'Type',
...@@ -127,17 +136,32 @@ class FluxAdmin extends AbstractAdmin ...@@ -127,17 +136,32 @@ class FluxAdmin extends AbstractAdmin
'show_filter' => true, 'show_filter' => true,
'field_type' => SChoiceType::class, 'field_type' => SChoiceType::class,
'field_options' => [ 'field_options' => [
'choices' => ['Cotisations' => Flux::TYPE_COTISATION, 'Achat de monnaie' => Flux::TYPE_ACHAT, 'Dépôt de billets' => Flux::TYPE_CHANGE, 'Retrait' => Flux::TYPE_RETRAIT, 'Transactions' => Flux::TYPE_TRANSACTION, 'Transferts' => Flux::TYPE_TRANSFERT, 'Vente' => Flux::TYPE_VENTE], 'choices' => $choices,
'placeholder' => 'Indifférent', 'placeholder' => 'Indifférent',
'expanded' => true, 'expanded' => true,
'multiple' => false, 'multiple' => false,
], ],
]) ]);
if ($ssaFriendlyTypeNames) {
$datagridMapper
->add('type', null, [ ->add('type', null, [
'label' => 'Type plus précis', 'label' => 'Type plus précis',
'advanced_filter' => false, 'advanced_filter' => false,
'show_filter' => true, 'show_filter' => true,
]) 'field_type' => SChoiceType::class,
'field_options' => [
'choices' => array_flip(Flux::ssaUsefulFriendlyTypeNames(true))
]
]);
} else {
$datagridMapper
->add('type', null, [
'label' => 'Type plus précis',
'advanced_filter' => false,
'show_filter' => true,
]);
}
$datagridMapper
->add('operateur', null, [ ->add('operateur', null, [
'label' => 'Operateur', 'label' => 'Operateur',
'advanced_filter' => false, 'advanced_filter' => false,
...@@ -182,9 +206,11 @@ class FluxAdmin extends AbstractAdmin ...@@ -182,9 +206,11 @@ class FluxAdmin extends AbstractAdmin
public function getExportFields() public function getExportFields()
{ {
$ssaFriendlyTypeNames = $this->getConfigurationPool()->getContainer()->getParameter('tav_env')
&& $this->getConfigurationPool()->getContainer()->getParameter('ssa_friendly_flux_type_names');
return [ return [
'Date' => 'created_at', 'Date' => 'created_at',
'Type' => 'type', 'Type' => $ssaFriendlyTypeNames ? 'ssaFriendlyTypeName' : 'type',
'Montant' => 'montant', 'Montant' => 'montant',
'Expediteur' => 'expediteur', 'Expediteur' => 'expediteur',
'Destinataire' => 'destinataire', 'Destinataire' => 'destinataire',
......
...@@ -24,10 +24,14 @@ use Twig\Environment; ...@@ -24,10 +24,14 @@ use Twig\Environment;
*/ */
class ReconversionMonaPrestatairesCommand extends Command class ReconversionMonaPrestatairesCommand extends Command
{ {
//Call with following crons :
//15 1 14,28 * * kohinos php /home/kohinos/kohinos/bin/console kohinos:ssa:reconversion-prestataires twice_a_month //Les reconversions doivent avoir lieu les dimanches soir qui précèdent les 14 et 28.
//17 1 28 * * kohinos php /home/kohinos/kohinos/bin/console kohinos:ssa:reconversion-prestataires once_a_month // On utilise les crons suivants :
//19 1 28 1,3,5,7,9,11 * kohinos php /home/kohinos/kohinos/bin/console kohinos:ssa:reconversion-prestataires once_every_two_month
//15 23 * * 0 [ "$(date +\%d)" -ge 08 -a "$(date +\%d)" -le 14 ] && kohinos php /home/kohinos/kohinos/bin/console kohinos:ssa:reconversion-prestataires twice_a_month
//15 23 * * 0 [ "$(date +\%d)" -ge 22 -a "$(date +\%d)" -le 28 ] && kohinos php /home/kohinos/kohinos/bin/console kohinos:ssa:reconversion-prestataires twice_a_month
//17 23 * * 0 [ "$(date +\%d)" -ge 22 -a "$(date +\%d)" -le 28 ] && kohinos php /home/kohinos/kohinos/bin/console kohinos:ssa:reconversion-prestataires once_a_month
//19 23 * 1,3,5,7,9,11 0 [ "$(date +\%d)" -ge 22 -a "$(date +\%d)" -le 28 ] && kohinos php /home/kohinos/kohinos/bin/console kohinos:ssa:reconversion-prestataires once_every_two_month
protected static $defaultName = 'kohinos:ssa:reconversion-prestataires'; protected static $defaultName = 'kohinos:ssa:reconversion-prestataires';
......
...@@ -261,7 +261,7 @@ class FluxController extends AbstractController ...@@ -261,7 +261,7 @@ class FluxController extends AbstractController
//projectDir is composer.json //projectDir is composer.json
$dir = $this->getParameter('kernel.project_dir') . "/reconversions"; $dir = $this->getParameter('kernel.project_dir') . "/reconversions";
if (!is_dir($dir)) { if (!is_dir($dir)) {
mkdir($dir, 0777, true); mkdir($dir, 0755, true);
} }
$path = $dir . "/" . $filename; $path = $dir . "/" . $filename;
......
...@@ -11,6 +11,7 @@ use App\Entity\TransactionAdherentAdherent; ...@@ -11,6 +11,7 @@ use App\Entity\TransactionAdherentAdherent;
use App\Entity\TransactionAdherentPrestataire; use App\Entity\TransactionAdherentPrestataire;
use App\Form\Type\AchatMonnaieAConfirmerAdherentFormType; use App\Form\Type\AchatMonnaieAConfirmerAdherentFormType;
use App\Form\Type\AchatMonnaieAdherentFormType; use App\Form\Type\AchatMonnaieAdherentFormType;
use App\Form\Type\AchatMonnaieAdherentRecurrentFormType;
use App\Form\Type\AdherentInfosFormType; use App\Form\Type\AdherentInfosFormType;
use App\Form\Type\TransactionAdherentAdherentFormType; use App\Form\Type\TransactionAdherentAdherentFormType;
use App\Form\Type\TransactionAdherentPrestataireFormType; use App\Form\Type\TransactionAdherentPrestataireFormType;
...@@ -122,6 +123,42 @@ class UserAdherentController extends FluxController ...@@ -122,6 +123,42 @@ class UserAdherentController extends FluxController
} }
/** /**
* Validations before proceeding to payment.
*
* Returns error message or null if no error.
*/
private function paiementCotisTavValidation($flux) {
//Look for existing recurring payment
if($reason = $this->tavCotisationsUtils->checkExistingRecurringPayment($flux)) {
return $reason;
}
// Look for existing cotisation
if ($this->tavCotisationsUtils->checkExistingCotisation($flux)) {
return "Cotisation déjà payée ce mois-ci.";
}
$destinataire = $flux->getDestinataire();
// Look for cotisation data depending on active process
if (true == $this->getParameter('household_based_allowance')) {
$cotisationAmount = $destinataire->getCotisationAmount();
if (is_null($cotisationAmount) || is_null($destinataire->getHouseholdAdultCount())) {
return "Opération impossible : votre profil est incomplet, informations de cotisation manquantes. Veuillez contacter un administrateur.";
}
} else {
$profile = $destinataire->getProfilDeCotisation();
if (is_null($profile)) {
return "Opération impossible : vous n'avez pas de profil de cotisation associé. Veuillez contacter un administrateur.";
}
}
return null;
}
/**
* @Route("/adherent/paiement-cotis-tav/", name="paiementCotisTav") * @Route("/adherent/paiement-cotis-tav/", name="paiementCotisTav")
* @IsGranted("ROLE_ADHERENT") * @IsGranted("ROLE_ADHERENT")
*/ */
...@@ -138,23 +175,11 @@ class UserAdherentController extends FluxController ...@@ -138,23 +175,11 @@ class UserAdherentController extends FluxController
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
$flux = $form->getData(); $flux = $form->getData();
// Look for existing cotisation $validationError = $this->paiementCotisTavValidation($flux);
if ($this->tavCotisationsUtils->checkExistingCotisation($flux)) { if (!is_null($validationError)) {
$this->addFlash( $this->addFlash(
'error', 'error',
$this->translator->trans('Cotisation déjà payée ce mois-ci.') $this->translator->trans($validationError)
);
return $this->redirectToRoute('index');
}
$destinataire = $flux->getDestinataire();
$profile = $destinataire->getProfilDeCotisation();
if (is_null($profile)) {
$this->addFlash(
'error',
$this->translator->trans('Opération impossible : vous n\'avez pas de profil de cotisation associé. Veuillez contacter un administrateur.')
); );
return $this->redirectToRoute('index'); return $this->redirectToRoute('index');
...@@ -193,6 +218,57 @@ class UserAdherentController extends FluxController ...@@ -193,6 +218,57 @@ class UserAdherentController extends FluxController
} }
/** /**
* @Route("/adherent/paiement-reccurent-cotis-tav/", name="paiementRecurrentCotisTav")
* @IsGranted("ROLE_ADHERENT")
*/
public function paiementRecurrentCotisTavAction(Request $request)
{
if (empty($this->getUser()) || empty($this->getUser()->getAdherent())) {
return $this->redirectToRoute('index');
}
$entity = new AchatMonnaieAdherent();
$form = $this->createForm(AchatMonnaieAdherentRecurrentFormType::class, $entity);
$form->handleRequest($request);
if (!$form->isValid()) {
foreach($form->getErrors(true) as $error) {
$this->addFlash('error', $error->getMessage());
}
return $this->redirectToRoute('index');
}
if ($form->isSubmitted() && $form->isValid()) {
$flux = $form->getData();
$validationError = $this->paiementCotisTavValidation($flux);
if (!is_null($validationError)) {
$this->addFlash(
'error',
$this->translator->trans($validationError)
);
return $this->redirectToRoute('index');
}
if (null == $flux->getDon() || 0 == $flux->getDon()->getMontant()) {
$flux->setDon(null);
}
// Redirect to payment
return $this->forward('App\Controller\PaymentController::preparePaymentAction', [
'form' => $form,
'type' => Payment::TYPE_PAIEMENT_RECURRENT_COTISATION_TAV
]);
}
return $this->render('@kohinos/flux/transaction.html.twig', [
'form' => $form->createView(),
'title' => $this->translator->trans('Payer sa cotisation'),
]);
}
/**
* @Route("/adherent/demande/achat-monnaie/", name="achatMonnaieAConfirmerAdherent") * @Route("/adherent/demande/achat-monnaie/", name="achatMonnaieAConfirmerAdherent")
* @IsGranted("ROLE_ADHERENT") * @IsGranted("ROLE_ADHERENT")
*/ */
......
...@@ -296,7 +296,7 @@ class UserController extends AbstractController ...@@ -296,7 +296,7 @@ class UserController extends AbstractController
$now = (new \Datetime('now'))->format('d/m/Y H:i:s'); $now = (new \Datetime('now'))->format('d/m/Y H:i:s');
$flux->setReference( $flux->setReference(
'Remboursement en Monnaie Solidaire du ' . $now . ', annule ' . $transactionAdherentPrestataire->getReference() 'Remboursement en Monnaie Solidaire du ' . $now . ' annule ' . $transactionAdherentPrestataire->getReference()
); );
$this->em->persist($flux); $this->em->persist($flux);
......
...@@ -649,4 +649,27 @@ abstract class Flux implements FluxInterface ...@@ -649,4 +649,27 @@ abstract class Flux implements FluxInterface
return ($this->getCreatedAt() ? $this->getCreatedAt()->format('d/m/Y H:i') . ' | ' : '') . ucwords($this->getParenttype()) . ' : ' . $this->getExpediteur() . ' => ' . $this->getDestinataire() . ' : ' . $this->getMontant() . '€'; return ($this->getCreatedAt() ? $this->getCreatedAt()->format('d/m/Y H:i') . ' | ' : '') . ucwords($this->getParenttype()) . ' : ' . $this->getExpediteur() . ' => ' . $this->getDestinataire() . ' : ' . $this->getMontant() . '€';
} }
public function ssaFriendlyTypeName()
{
$friendlyTypeNames = Flux::ssaUsefulFriendlyTypeNames();
return array_key_exists($this->type,$friendlyTypeNames) ?
$friendlyTypeNames[$this->type]
: $this->type;
}
public static function ssaUsefulFriendlyTypeNames($differentiateAchatsDeMonaPayzenOrComptoir = false)
{
return [
VenteEmlc::TYPE_VENTE_EMLC_ADHERENT => "Achat de MonA via cotisation" . ($differentiateAchatsDeMonaPayzenOrComptoir ? " (au comptoir)" : ""),
AchatMonnaie::TYPE_ACHAT_ADHERENT => "Achat de MonA via cotisation" . ($differentiateAchatsDeMonaPayzenOrComptoir ? " (via Payzen)" : ""),
CotisationTavApplication::TYPE_REVERSEMENT_COTISATION_ADHERENT => "Allocation complémentaire de la caisse",
CotisationTavApplication::TYPE_PRELEVEMENT_COTISATION_ADHERENT => "Réduction de l'allocation",
Don::TYPE_DON_ADHERENT => Don::TYPE_DON_ADHERENT,
Don::TYPE_DON_PRESTATAIRE => Don::TYPE_DON_PRESTATAIRE,
Reconversion::TYPE_RECONVERSION_PRESTATAIRE => Reconversion::TYPE_RECONVERSION_PRESTATAIRE,
Transaction::TYPE_TRANSACTION_PRESTATAIRE_ADHERENT => Transaction::TYPE_TRANSACTION_PRESTATAIRE_ADHERENT,
Transaction::TYPE_TRANSACTION_ADHERENT_PRESTATAIRE => Transaction::TYPE_TRANSACTION_ADHERENT_PRESTATAIRE
];
}
} }
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
namespace App\Entity; namespace App\Entity;
use DateTime;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Payum\Core\Model\Payment as BasePayment; use Payum\Core\Model\Payment as BasePayment;
use Ramsey\Uuid\Doctrine\UuidGenerator; use Ramsey\Uuid\Doctrine\UuidGenerator;
...@@ -18,6 +19,7 @@ class Payment extends BasePayment ...@@ -18,6 +19,7 @@ class Payment extends BasePayment
const TYPE_COTISATION_PRESTA = 'cotisation_presta'; const TYPE_COTISATION_PRESTA = 'cotisation_presta';
const TYPE_ADHESION = 'adhesion'; const TYPE_ADHESION = 'adhesion';
const TYPE_PAIEMENT_COTISATION_TAV = 'paiement_cotisation_tav'; const TYPE_PAIEMENT_COTISATION_TAV = 'paiement_cotisation_tav';
const TYPE_PAIEMENT_RECURRENT_COTISATION_TAV = 'paiement_recurrent_cotisation_tav';
/** /**
* @var \Ramsey\Uuid\UuidInterface * @var \Ramsey\Uuid\UuidInterface
...@@ -53,6 +55,26 @@ class Payment extends BasePayment ...@@ -53,6 +55,26 @@ class Payment extends BasePayment
protected $extra_data; protected $extra_data;
/** /**
* @ORM\Column(type="boolean", nullable=True)
*/
private $isRecurrent;
/**
* @ORM\Column(type="integer", nullable=true)
*/
private $recurrenceMonthsCount;
/**
* @ORM\Column(type="integer", nullable=true)
*/
private $recurrenceMonthDay;
/**
* @ORM\Column(type="integer", nullable=true)
*/
private $recurrenceAmount;
/**
* @return string * @return string
*/ */
public function getStatus(): ?string public function getStatus(): ?string
...@@ -111,4 +133,104 @@ class Payment extends BasePayment ...@@ -111,4 +133,104 @@ class Payment extends BasePayment
return $this; return $this;
} }
public function getIsRecurrent(): ?bool
{
return $this->isRecurrent;
}
public function setIsRecurrent(bool $isRecurrent): self
{
$this->isRecurrent = $isRecurrent;
return $this;
}
public function getRecurrenceMonthsCount(): ?int
{
return $this->recurrenceMonthsCount;
}
public function setRecurrenceMonthsCount(?int $recurrenceMonthsCount): self
{
$this->recurrenceMonthsCount = $recurrenceMonthsCount;
return $this;
}
public function getRecurrenceMonthDay(): ?int
{
return $this->recurrenceMonthDay;
}
public function setRecurrenceMonthDay(?int $recurrenceMonthDay): self
{
$this->recurrenceMonthDay = $recurrenceMonthDay;
return $this;
}
public function getRecurrenceAmount(): ?int
{
return $this->recurrenceAmount;
}
public function setRecurrenceAmount(?int $recurrenceAmount): self
{
$this->recurrenceAmount = $recurrenceAmount;
return $this;
}
/**
* Return null in case of error
* Returns true if payment is already ended or CB expired
* Returns false otherwise
*
* @param $reason
* @return bool|null
*/
public function isRecurringPaymentEndedOrExpired(&$reason)
{
if(!$this->details
|| !array_key_exists('vads_effective_creation_date',$this->details)
|| !array_key_exists('vads_expiry_year',$this->details)
|| !array_key_exists('vads_effective_creation_date',$this->details)
) {
$reason = "Attribut détails vide ou clés absentes.";
return null;
}
$firstDayOfCreationMonth = DateTime::createFromFormat(
'Ymd',substr($this->details['vads_effective_creation_date'],0,6) . "01"
);
if(!$firstDayOfCreationMonth) {
$reason = "Error parsing vads_effective_creation_date : " . $this->details['vads_effective_creation_date'];
return null;
}
$day = $this->getRecurrenceMonthDay() ? $this->getRecurrenceMonthDay() - 1 : 0; //if not set assume first day of month (not a big deal anyway)
$dateOfFirstOccurenceAfterInitialPayment = $firstDayOfCreationMonth->modify(
"+" . $day . " days next month"
);
$paymentEndDate = $this->getRecurrenceMonthsCount() ?
$dateOfFirstOccurenceAfterInitialPayment->modify(
"+" . ($this->getRecurrenceMonthsCount() - 1) . " months" //minus one because initial payment is not considered by payzen as the first occurence
)
: null; //assume no end date if recurrenceMonthsCount not set
//Now check expiry date
$paymentEndDateDueToExpiry = new DateTime();
$paymentEndDateDueToExpiry->setDate($this->details['vads_expiry_year'],$this->details['vads_expiry_month'],$day+1);
$now = new DateTime();
if($paymentEndDate && $paymentEndDate < $paymentEndDateDueToExpiry) {
//Compare now with payment end date
$reason = "Paiement récurrent en cours jusqu'à dernière échéance le " . $paymentEndDate->format('d/m/Y') . ".";
return $now >= $paymentEndDate;
} else {
//Compare now with expiry date
$reason = "Paiement récurrent en cours jusqu'à dernière échéance le " . $paymentEndDateDueToExpiry->format('d/m/Y') . " en raison de l'expiration du moyen de paiement.";
return $now >= $paymentEndDateDueToExpiry;
}
}
} }
...@@ -28,6 +28,7 @@ use App\Entity\User; ...@@ -28,6 +28,7 @@ use App\Entity\User;
use App\Form\Type\AchatMonnaieAConfirmerAdherentFormType; use App\Form\Type\AchatMonnaieAConfirmerAdherentFormType;
use App\Form\Type\AchatMonnaieAConfirmerPrestataireFormType; use App\Form\Type\AchatMonnaieAConfirmerPrestataireFormType;
use App\Form\Type\AchatMonnaieAdherentFormType; use App\Form\Type\AchatMonnaieAdherentFormType;
use App\Form\Type\AchatMonnaieAdherentRecurrentFormType;
use App\Form\Type\AchatMonnaiePrestataireFormType; use App\Form\Type\AchatMonnaiePrestataireFormType;
use App\Form\Type\AdherentInfosFormType; use App\Form\Type\AdherentInfosFormType;
use App\Form\Type\ChangeAdherentComptoirFormType; use App\Form\Type\ChangeAdherentComptoirFormType;
...@@ -474,6 +475,19 @@ class FormFactory ...@@ -474,6 +475,19 @@ class FormFactory
return $form->createView(); return $form->createView();
} }
public function getPaiementRecurrentCotisationTAVForm(User $user)
{
if (empty($user) || !$user->isGranted('ROLE_ADHERENT')) {
throw new \Exception('[FORM 25] Opération impossible !');
}
$entity = new AchatMonnaieAdherent();
$entity->setOperateur($user);
$form = $this->ff->create(AchatMonnaieAdherentRecurrentFormType::class, $entity, ['action' => $this->router->generate('paiementRecurrentCotisTav')]);
return $form->createView();
}
public function getComptoirEncaisserDonAdherentForm(User $user) public function getComptoirEncaisserDonAdherentForm(User $user)
{ {
if (empty($user) || empty($this->session->get('_comptoirgere'))) { if (empty($user) || empty($this->session->get('_comptoirgere'))) {
......
<?php
namespace App\Form\Type;
use App\Entity\AchatMonnaieAdherent;
use App\Entity\Adherent;
use App\Entity\DonAdherent;
use App\Entity\GlobalParameter;
use App\Entity\Prestataire;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\GreaterThanOrEqual;
class AchatMonnaieAdherentRecurrentFormType extends AchatMonnaieAdherentFormType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$now = new \DateTime();
//force prelevement to occur on same day as today to make sure next prelevement occurs next month
//if today is after the 28th, use the 28th so that prelevement always occurs on same date
$jourPrelevement = min($now->format('d'),28);
$builder
->add('nombreMois', IntegerType::class, [
'label' => 'Nombre d\'échéances désirées : ',
'required' => true,
'mapped' => false,
'constraints' => [
new GreaterThanOrEqual(['value' => 2, 'message' => "Le nombre d'échéances doit être au moins égal à 2."]),
],
'help' => "Une échéance par mois.
Le premier paiement est inclus dans le nombre d'échéances.
Le nombre d'échéance réel pourra être diminué selon la date d'expiration du moyen de paiement utilisé.
Pour demander une interruption anticipée, merci de contacter la caisse.",
'attr' => ['autocomplete' => 'off']
])
->add('jourPrelevement', ChoiceType::class, [
'label' => 'Jour du prélèvement dans le mois : ',
'choices' => [strval($jourPrelevement) => $jourPrelevement],
'data' => $jourPrelevement,
'required' => true,
'mapped' => false,
'attr' => ['autocomplete' => 'off', 'readonly' => true],
'help' => "Pour simplifier le traitement de l'information, le jour de prélèvement est fixé au jour du premier paiement ou au plus tard le 28 du mois."
])
->remove('saveHelloAsso')
->remove('save')
->add('save', SubmitType::class, [
'label' => 'Payer en CB mon premier paiement et créer un paiement récurrent',
'translation_domain' => 'messages',
'attr' => [
'class' => 'btn-primary btn achatCBSubmit',
],
])
->remove('reference')
->add('reference', HiddenType::class, [
'data' => 'Achat monnaie en CB (récurrent) Adhérent',
])
;
$builder
;
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'class' => AchatMonnaieAdherent::class,
]);
}
public function getParent()
{
return AchatMonnaieAdherentFormType::class;
}
public function getBlockPrefix()
{
return 'formAchatMonnaieAdherentRecurrent';
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240321105448 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE payment ADD is_recurrent TINYINT(1) DEFAULT \'0\' NOT NULL, ADD recurrence_months_count INT DEFAULT NULL, ADD recurrence_month_day INT DEFAULT NULL, ADD recurrence_amount INT DEFAULT NULL');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE payment DROP is_recurrent, DROP recurrence_months_count, DROP recurrence_month_day, DROP recurrence_amount');
}
}
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20240325100723 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE payment CHANGE is_recurrent is_recurrent TINYINT(1) DEFAULT NULL');
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:personal_data)\'');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE payment CHANGE is_recurrent is_recurrent TINYINT(1) DEFAULT \'0\' NOT NULL');
$this->addSql('ALTER TABLE prestataire CHANGE iban iban LONGTEXT CHARACTER SET utf8mb3 DEFAULT NULL COLLATE `utf8mb3_general_ci` COMMENT \'(DC2Type:personal_data)\'');
}
}
...@@ -55,6 +55,7 @@ class FormExtension extends AbstractExtension ...@@ -55,6 +55,7 @@ class FormExtension extends AbstractExtension
new \Twig_SimpleFunction('getSetPaymentCodeForm', [$this, 'getSetPaymentCodeForm']), new \Twig_SimpleFunction('getSetPaymentCodeForm', [$this, 'getSetPaymentCodeForm']),
new \Twig_SimpleFunction('getComptoirEncaisserCotisationForm', [$this, 'getComptoirEncaisserCotisationForm']), new \Twig_SimpleFunction('getComptoirEncaisserCotisationForm', [$this, 'getComptoirEncaisserCotisationForm']),
new \Twig_SimpleFunction('getPayerCotisationTAVForm', [$this, 'getPayerCotisationTAVForm']), new \Twig_SimpleFunction('getPayerCotisationTAVForm', [$this, 'getPayerCotisationTAVForm']),
new \Twig_SimpleFunction('getPaiementRecurrentCotisationTAVForm', [$this, 'getPaiementRecurrentCotisationTAVForm']),
new \Twig_SimpleFunction('getComptoirEncaisserDonAdherentForm', [$this, 'getComptoirEncaisserDonAdherentForm']) new \Twig_SimpleFunction('getComptoirEncaisserDonAdherentForm', [$this, 'getComptoirEncaisserDonAdherentForm'])
]; ];
} }
...@@ -219,6 +220,11 @@ class FormExtension extends AbstractExtension ...@@ -219,6 +220,11 @@ class FormExtension extends AbstractExtension
return $this->container->get('app.formfactory')->getPayerCotisationTAVForm($user); return $this->container->get('app.formfactory')->getPayerCotisationTAVForm($user);
} }
public function getPaiementRecurrentCotisationTAVForm(User $user)
{
return $this->container->get('app.formfactory')->getPaiementRecurrentCotisationTAVForm($user);
}
public function getComptoirEncaisserDonAdherentForm(User $user) public function getComptoirEncaisserDonAdherentForm(User $user)
{ {
return $this->container->get('app.formfactory')->getComptoirEncaisserDonAdherentForm($user); return $this->container->get('app.formfactory')->getComptoirEncaisserDonAdherentForm($user);
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
namespace App\Utils; namespace App\Utils;
use App\Entity\Adherent; use App\Entity\Adherent;
use App\Entity\Payment;
use App\Entity\Siege; use App\Entity\Siege;
use App\Entity\Flux; use App\Entity\Flux;
use App\Entity\CotisationTavReversement; use App\Entity\CotisationTavReversement;
...@@ -44,6 +45,23 @@ class TAVCotisationUtils ...@@ -44,6 +45,23 @@ class TAVCotisationUtils
return count($existing) > 0; return count($existing) > 0;
} }
public function checkExistingRecurringPayment(Flux $flux)
{
$recurringPayments = $this->em->getRepository(Payment::class)->findBy([
'isRecurrent' => true,
'clientEmail' => $flux->getDestinataire()->getUser()->getEmail(),
]);
$res = "";
foreach($recurringPayments as $p) {
$reason = "";
if($p->isRecurringPaymentEndedOrExpired($reason) !== true) {
$res .= ($reason . " ");
}
}
return $res;
}
/** /**
* First method to calculate allowance: * First method to calculate allowance:
* according to a contribution rate defined in user's profile (ProfilDeCotisation). * according to a contribution rate defined in user's profile (ProfilDeCotisation).
...@@ -168,14 +186,14 @@ class TAVCotisationUtils ...@@ -168,14 +186,14 @@ class TAVCotisationUtils
$fluxCotis->setExpediteur($siege); $fluxCotis->setExpediteur($siege);
$fluxCotis->setDestinataire($adherent); $fluxCotis->setDestinataire($adherent);
$fluxCotis->setMontant($amountDiff); $fluxCotis->setMontant($amountDiff);
$fluxCotis->setReference("Reversement du complément de cotisation après paiement de " . $cotisationAmount . "€ pour une allocation de " . $mlcAllowanceAmount . " MonA."); $fluxCotis->setReference("Versement de l'allocation complémentaire après paiement de " . $cotisationAmount . "€ pour atteindre une allocation de " . $mlcAllowanceAmount . " MonA.");
} else { } else {
// User should receive less than he•she paid: fetch the difference from his account // User should receive less than he•she paid: fetch the difference from his account
$fluxCotis = new CotisationTavPrelevement(); $fluxCotis = new CotisationTavPrelevement();
$fluxCotis->setExpediteur($adherent); $fluxCotis->setExpediteur($adherent);
$fluxCotis->setDestinataire($siege); $fluxCotis->setDestinataire($siege);
$fluxCotis->setMontant(-$amountDiff); $fluxCotis->setMontant(-$amountDiff);
$fluxCotis->setReference("Prélèvement du complément de cotisation après paiement de " . $cotisationAmount . "€ pour une allocation de " . $mlcAllowanceAmount . " MonA."); $fluxCotis->setReference("Réduction de l'allocation correspondant à un paiement de " . $cotisationAmount . "€ pour atteindre une allocation de " . $mlcAllowanceAmount . " MonA.");
} }
$fluxCotis->setOperateur($flux->getOperateur()); $fluxCotis->setOperateur($flux->getOperateur());
...@@ -205,7 +223,7 @@ class TAVCotisationUtils ...@@ -205,7 +223,7 @@ class TAVCotisationUtils
$flux->setExpediteur($adherent); $flux->setExpediteur($adherent);
$flux->setDestinataire($siege); $flux->setDestinataire($siege);
$flux->setMontant(-$amountDiff); $flux->setMontant(-$amountDiff);
$flux->setReference("Prélèvement pour ramener le solde de " . $balance . "MonA sous le plafond de " . $ceiling . " MonA."); $flux->setReference("Prélèvement pour ramener le solde de " . $balance . " MonA sous le plafond de " . $ceiling . " MonA.");
$flux->setOperateur($this->security->getUser()); $flux->setOperateur($this->security->getUser());
$flux->setRole($this->security->getUser()->getGroups()[0]->__toString()); $flux->setRole($this->security->getUser()->getGroups()[0]->__toString());
$flux->setMoyen(MoyenEnum::MOYEN_EMLC); $flux->setMoyen(MoyenEnum::MOYEN_EMLC);
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
{% endblock blocktitle %} {% endblock blocktitle %}
{% block blockcontent %} {% block blockcontent %}
{% set form = getPayerCotisationTAVForm(app.user) %} {% set form = getPayerCotisationTAVForm(app.user) %}
{% set formRecurrentPayment = getPaiementRecurrentCotisationTAVForm(app.user) %}
{% if form.montant.vars.value == false and not household_based_allowance %} {% if form.montant.vars.value == false and not household_based_allowance %}
<p>{{ 'Vous n\'avez pas de profil de cotisation associé, vous ne pouvez donc pas payer de cotisation.'|trans }}</p> <p>{{ 'Vous n\'avez pas de profil de cotisation associé, vous ne pouvez donc pas payer de cotisation.'|trans }}</p>
...@@ -16,9 +17,15 @@ ...@@ -16,9 +17,15 @@
<p> <p>
{{ 'Montant de la cotisation à payer'|trans }} : <span class="paiement_cotisation_montant">{{ form.montant.vars.value }}</span> {{ 'Montant de la cotisation à payer'|trans }} : <span class="paiement_cotisation_montant">{{ form.montant.vars.value }}</span>
</p> </p>
<select name="tav-cotisation-payment-type" id="tav-cotisation-payment-type" class="form-control" autocomplete="off">
<option value="">Choisir une option de paiement</option>
<option value="instant-payment">Paiement immédiat</option>
<option value="recurrent-payment">Paiement récurrent</option>
</select>
<div id="tav-cotisation-instant-payment-container" class="tav-cotisation-payment-form">
{{form_start(form)}} {{form_start(form)}}
{% if form.don is defined %} {% if form.don is defined %}
{% include '@kohinos/tav/block/adherent_payer_cotisation_don.html.twig' %} {% include '@kohinos/tav/block/adherent_payer_cotisation_don.html.twig' with {'parentForm': form} %}
<hr/> <hr/>
<p><b>{{ 'TOTAL A PAYER'|trans }} : <span class="achat_monnaie_montant_total">{{ form.montant.vars.value }}</span></b></p> <p><b>{{ 'TOTAL A PAYER'|trans }} : <span class="achat_monnaie_montant_total">{{ form.montant.vars.value }}</span></b></p>
{% endif %} {% endif %}
...@@ -29,6 +36,22 @@ ...@@ -29,6 +36,22 @@
{{ form_widget(form.saveHelloAsso) }} {{ form_widget(form.saveHelloAsso) }}
{% endif %} {% endif %}
{{form_end(form)}} {{form_end(form)}}
</div>
<div id="tav-cotisation-recurrent-payment-container" class="tav-cotisation-payment-form">
{{form_start(formRecurrentPayment)}}
{{ form_row(formRecurrentPayment.nombreMois) }}
{{ form_row(formRecurrentPayment.jourPrelevement) }}
{% if formRecurrentPayment.don is defined %}
{% include '@kohinos/tav/block/adherent_payer_cotisation_don.html.twig' with {'parentForm': formRecurrentPayment} %}
<hr/>
<p><b>{{ 'TOTAL PREMIER PAIEMENT'|trans }} : <span class="achat_monnaie_premier_montant_total">{{ formRecurrentPayment.montant.vars.value }}</span></b></p>
<p><b>{{ 'ÉCHÉANCES SUIVANTES'|trans }} : <span class="achat_monnaie_montant_total">{{ formRecurrentPayment.montant.vars.value }}</span></b></p>
{% endif %}
{% if formRecurrentPayment.save is defined %}
{{ form_widget(formRecurrentPayment.save) }}
{% endif %}
{{form_end(formRecurrentPayment)}}
</div>
{% endif %} {% endif %}
......
...@@ -4,5 +4,5 @@ ...@@ -4,5 +4,5 @@
En plus de ma cotisation je souhaite faire un don pour ce mois En plus de ma cotisation je souhaite faire un don pour ce mois
{% endblock blocktitle %} {% endblock blocktitle %}
{% block blockcontent %} {% block blockcontent %}
{{ form_row(form.don, {row_attr:{style: 'max-width: 200px;margin: 0 auto;'}}) }} {{ form_row(parentForm.don, {row_attr:{style: 'max-width: 200px;margin: 0 auto;'}}) }}
{% endblock blockcontent %} {% endblock blockcontent %}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment